Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
N
Neos
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
love_飞影
Neos
Commits
573f22bb
Commit
573f22bb
authored
Oct 29, 2022
by
Chunchi Che
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'optimize/rerange' into 'main'
add adapter layer See merge request
mycard/Neos!9
parents
c9dfd8b0
05078df6
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
101 additions
and
91 deletions
+101
-91
src/api/ocgcore/ocgAdapter/adapter.ts
src/api/ocgcore/ocgAdapter/adapter.ts
+57
-0
src/api/ocgcore/ocgAdapter/packet.ts
src/api/ocgcore/ocgAdapter/packet.ts
+10
-8
src/api/ocgcore/ocgAdapter/stoc/stocChat.ts
src/api/ocgcore/ocgAdapter/stoc/stocChat.ts
+3
-3
src/api/ocgcore/ocgAdapter/stoc/stocHsPlayerChange.ts
src/api/ocgcore/ocgAdapter/stoc/stocHsPlayerChange.ts
+3
-3
src/api/ocgcore/ocgAdapter/stoc/stocHsPlayerEnter.ts
src/api/ocgcore/ocgAdapter/stoc/stocHsPlayerEnter.ts
+3
-3
src/api/ocgcore/ocgAdapter/stoc/stocHsWatchChange.ts
src/api/ocgcore/ocgAdapter/stoc/stocHsWatchChange.ts
+3
-3
src/api/ocgcore/ocgAdapter/stoc/stocJoinGame.ts
src/api/ocgcore/ocgAdapter/stoc/stocJoinGame.ts
+3
-3
src/api/ocgcore/ocgAdapter/stoc/stocTypeChange.ts
src/api/ocgcore/ocgAdapter/stoc/stocTypeChange.ts
+3
-3
src/api/ocgcore/ocgHelper.ts
src/api/ocgcore/ocgHelper.ts
+12
-10
src/service/onSocketMessage.ts
src/service/onSocketMessage.ts
+4
-55
No files found.
src/api/ocgcore/ocgAdapter/adapter.ts
0 → 100644
View file @
573f22bb
import
{
StocAdapter
,
ygoProPacket
}
from
"
./packet
"
;
import
{
ygopro
}
from
"
../idl/ocgcore
"
;
import
{
STOC_CHAT
,
STOC_HS_PLAYER_CHANGE
,
STOC_HS_PLAYER_ENTER
,
STOC_HS_WATCH_CHANGE
,
STOC_JOIN_GAME
,
STOC_TYPE_CHANGE
,
}
from
"
./protoDecl
"
;
import
StocChat
from
"
./stoc/stocChat
"
;
import
StocJoinGame
from
"
./stoc/stocJoinGame
"
;
import
StocHsPlayerEnter
from
"
./stoc/stocHsPlayerEnter
"
;
import
StocHsPlayerChange
from
"
./stoc/stocHsPlayerChange
"
;
import
StocHsWatchChange
from
"
./stoc/stocHsWatchChange
"
;
import
StocTypeChange
from
"
./stoc/stocTypeChange
"
;
export
function
adaptStoc
(
packet
:
ygoProPacket
):
ygopro
.
YgoStocMsg
{
let
pb
=
new
ygopro
.
YgoStocMsg
({});
switch
(
packet
.
proto
)
{
case
STOC_JOIN_GAME
:
{
pb
=
new
StocJoinGame
(
packet
).
upcast
();
break
;
}
case
STOC_CHAT
:
{
pb
=
new
StocChat
(
packet
).
upcast
();
break
;
}
case
STOC_HS_PLAYER_ENTER
:
{
pb
=
new
StocHsPlayerEnter
(
packet
).
upcast
();
break
;
}
case
STOC_HS_PLAYER_CHANGE
:
{
pb
=
new
StocHsPlayerChange
(
packet
).
upcast
();
break
;
}
case
STOC_HS_WATCH_CHANGE
:
{
pb
=
new
StocHsWatchChange
(
packet
).
upcast
();
break
;
}
case
STOC_TYPE_CHANGE
:
{
pb
=
new
StocTypeChange
(
packet
).
upcast
();
break
;
}
default
:
{
break
;
}
}
return
pb
;
}
src/api/ocgcore/ocgAdapter/packet.ts
View file @
573f22bb
...
@@ -24,10 +24,8 @@ export class ygoProPacket {
...
@@ -24,10 +24,8 @@ export class ygoProPacket {
return
array
;
return
array
;
}
}
}
export
class
ygoArrayBuilder
extends
ygoProPacket
{
static
deserialize
(
array
:
ArrayBuffer
):
ygoProPacket
{
constructor
(
array
:
ArrayBuffer
)
{
try
{
try
{
if
(
array
.
byteLength
<
PACKET_MIN_LEN
)
{
if
(
array
.
byteLength
<
PACKET_MIN_LEN
)
{
throw
new
Error
(
throw
new
Error
(
...
@@ -35,7 +33,7 @@ export class ygoArrayBuilder extends ygoProPacket {
...
@@ -35,7 +33,7 @@ export class ygoArrayBuilder extends ygoProPacket {
);
);
}
}
}
catch
(
e
)
{
}
catch
(
e
)
{
console
.
log
(
"
[e][ygoProPacket][constructor]
"
+
e
);
console
.
log
(
e
);
}
}
const
dataView
=
new
DataView
(
array
);
const
dataView
=
new
DataView
(
array
);
...
@@ -44,12 +42,16 @@ export class ygoArrayBuilder extends ygoProPacket {
...
@@ -44,12 +42,16 @@ export class ygoArrayBuilder extends ygoProPacket {
const
proto
=
dataView
.
getInt8
(
2
);
const
proto
=
dataView
.
getInt8
(
2
);
const
exData
=
array
.
slice
(
3
,
packetLen
+
2
);
const
exData
=
array
.
slice
(
3
,
packetLen
+
2
);
super
(
packetLen
,
proto
,
new
Uint8Array
(
exData
));
return
new
ygoProPacket
(
packetLen
,
proto
,
new
Uint8Array
(
exData
));
}
}
}
}
export
interface
ygoProtobuf
{
export
interface
StocAdapter
{
readonly
packet
:
ygoProPacket
;
upcast
():
ygopro
.
YgoStocMsg
;
}
export
interface
CtosAdapter
{
readonly
protobuf
:
ygopro
.
YgoCtosMsg
;
adapt
():
ygopro
.
YgoStocMsg
;
downcast
():
ygoProPacket
;
}
}
src/api/ocgcore/ocgAdapter/stoc/stocChat.ts
View file @
573f22bb
import
{
ygopro
}
from
"
../../idl/ocgcore
"
;
import
{
ygopro
}
from
"
../../idl/ocgcore
"
;
import
{
ygoProPacket
,
ygoProtobuf
}
from
"
../packet
"
;
import
{
ygoProPacket
,
StocAdapter
}
from
"
../packet
"
;
export
default
class
StocChatPB
implements
ygoProtobuf
{
export
default
class
chatAdapter
implements
StocAdapter
{
packet
:
ygoProPacket
;
packet
:
ygoProPacket
;
constructor
(
packet
:
ygoProPacket
)
{
constructor
(
packet
:
ygoProPacket
)
{
this
.
packet
=
packet
;
this
.
packet
=
packet
;
}
}
adap
t
():
ygopro
.
YgoStocMsg
{
upcas
t
():
ygopro
.
YgoStocMsg
{
const
player
=
new
DataView
(
this
.
packet
.
exData
.
buffer
).
getInt16
(
0
,
true
);
const
player
=
new
DataView
(
this
.
packet
.
exData
.
buffer
).
getInt16
(
0
,
true
);
const
decoder
=
new
TextDecoder
(
"
utf-16
"
);
const
decoder
=
new
TextDecoder
(
"
utf-16
"
);
...
...
src/api/ocgcore/ocgAdapter/stoc/stocHsPlayerChange.ts
View file @
573f22bb
import
{
ygopro
}
from
"
../../idl/ocgcore
"
;
import
{
ygopro
}
from
"
../../idl/ocgcore
"
;
import
{
ygoProPacket
,
ygoProtobuf
}
from
"
../packet
"
;
import
{
ygoProPacket
,
StocAdapter
}
from
"
../packet
"
;
export
default
class
StocHsPlayerChange
implements
ygoProtobuf
{
export
default
class
hsPlayerChangeAdapter
implements
StocAdapter
{
packet
:
ygoProPacket
;
packet
:
ygoProPacket
;
constructor
(
packet
:
ygoProPacket
)
{
constructor
(
packet
:
ygoProPacket
)
{
this
.
packet
=
packet
;
this
.
packet
=
packet
;
}
}
adap
t
():
ygopro
.
YgoStocMsg
{
upcas
t
():
ygopro
.
YgoStocMsg
{
const
pb
=
new
ygopro
.
StocHsPlayerChange
({});
const
pb
=
new
ygopro
.
StocHsPlayerChange
({});
pb
.
state
=
ygopro
.
StocHsPlayerChange
.
State
.
UNKNOWN
;
pb
.
state
=
ygopro
.
StocHsPlayerChange
.
State
.
UNKNOWN
;
...
...
src/api/ocgcore/ocgAdapter/stoc/stocHsPlayerEnter.ts
View file @
573f22bb
import
{
ygopro
}
from
"
../../idl/ocgcore
"
;
import
{
ygopro
}
from
"
../../idl/ocgcore
"
;
import
{
ygoProPacket
,
ygoProtobuf
}
from
"
../packet
"
;
import
{
ygoProPacket
,
StocAdapter
}
from
"
../packet
"
;
import
{
UTF16_BUFFER_MAX_LEN
}
from
"
../util
"
;
import
{
UTF16_BUFFER_MAX_LEN
}
from
"
../util
"
;
const
UINT8_PER_UINT16
=
2
;
const
UINT8_PER_UINT16
=
2
;
export
default
class
StocHsPlayerEnter
implements
ygoProtobuf
{
export
default
class
hsPlayerEnterAdapter
implements
StocAdapter
{
packet
:
ygoProPacket
;
packet
:
ygoProPacket
;
constructor
(
packet
:
ygoProPacket
)
{
constructor
(
packet
:
ygoProPacket
)
{
this
.
packet
=
packet
;
this
.
packet
=
packet
;
}
}
adap
t
():
ygopro
.
YgoStocMsg
{
upcas
t
():
ygopro
.
YgoStocMsg
{
const
exData
=
this
.
packet
.
exData
;
const
exData
=
this
.
packet
.
exData
;
const
decoder
=
new
TextDecoder
(
"
utf-16
"
);
const
decoder
=
new
TextDecoder
(
"
utf-16
"
);
...
...
src/api/ocgcore/ocgAdapter/stoc/stocHsWatchChange.ts
View file @
573f22bb
import
{
ygopro
}
from
"
../../idl/ocgcore
"
;
import
{
ygopro
}
from
"
../../idl/ocgcore
"
;
import
{
ygoProPacket
,
ygoProtobuf
}
from
"
../packet
"
;
import
{
ygoProPacket
,
StocAdapter
}
from
"
../packet
"
;
export
default
class
StocHsWatchChange
implements
ygoProtobuf
{
export
default
class
hsWatchChangeAdapter
implements
StocAdapter
{
packet
:
ygoProPacket
;
packet
:
ygoProPacket
;
constructor
(
packet
:
ygoProPacket
)
{
constructor
(
packet
:
ygoProPacket
)
{
this
.
packet
=
packet
;
this
.
packet
=
packet
;
}
}
adap
t
():
ygopro
.
YgoStocMsg
{
upcas
t
():
ygopro
.
YgoStocMsg
{
const
count
=
new
DataView
(
this
.
packet
.
exData
.
buffer
).
getUint16
(
0
,
true
);
const
count
=
new
DataView
(
this
.
packet
.
exData
.
buffer
).
getUint16
(
0
,
true
);
return
new
ygopro
.
YgoStocMsg
({
return
new
ygopro
.
YgoStocMsg
({
...
...
src/api/ocgcore/ocgAdapter/stoc/stocJoinGame.ts
View file @
573f22bb
import
{
ygopro
}
from
"
../../idl/ocgcore
"
;
import
{
ygopro
}
from
"
../../idl/ocgcore
"
;
import
{
ygoProPacket
,
ygoProtobuf
}
from
"
../packet
"
;
import
{
ygoProPacket
,
StocAdapter
}
from
"
../packet
"
;
export
default
class
StocJoinGamePB
implements
ygoProtobuf
{
export
default
class
joinGameAdapter
implements
StocAdapter
{
packet
:
ygoProPacket
;
packet
:
ygoProPacket
;
constructor
(
packet
:
ygoProPacket
)
{
constructor
(
packet
:
ygoProPacket
)
{
this
.
packet
=
packet
;
this
.
packet
=
packet
;
}
}
adap
t
():
ygopro
.
YgoStocMsg
{
upcas
t
():
ygopro
.
YgoStocMsg
{
// todo
// todo
return
new
ygopro
.
YgoStocMsg
({
return
new
ygopro
.
YgoStocMsg
({
stoc_join_game
:
new
ygopro
.
StocJoinGame
({}),
stoc_join_game
:
new
ygopro
.
StocJoinGame
({}),
...
...
src/api/ocgcore/ocgAdapter/stoc/stocTypeChange.ts
View file @
573f22bb
import
{
ygopro
}
from
"
../../idl/ocgcore
"
;
import
{
ygopro
}
from
"
../../idl/ocgcore
"
;
import
{
ygoProPacket
,
ygoProtobuf
}
from
"
../packet
"
;
import
{
ygoProPacket
,
StocAdapter
}
from
"
../packet
"
;
export
default
class
StocTypeChange
implements
ygoProtobuf
{
export
default
class
typeChangeAdapter
implements
StocAdapter
{
packet
:
ygoProPacket
;
packet
:
ygoProPacket
;
constructor
(
packet
:
ygoProPacket
)
{
constructor
(
packet
:
ygoProPacket
)
{
this
.
packet
=
packet
;
this
.
packet
=
packet
;
}
}
adap
t
():
ygopro
.
YgoStocMsg
{
upcas
t
():
ygopro
.
YgoStocMsg
{
const
type_
=
new
DataView
(
this
.
packet
.
exData
.
buffer
).
getUint8
(
0
);
const
type_
=
new
DataView
(
this
.
packet
.
exData
.
buffer
).
getUint8
(
0
);
const
isHost
=
((
type_
>>
4
)
&
0xf
)
!=
0
;
const
isHost
=
((
type_
>>
4
)
&
0xf
)
!=
0
;
...
...
src/api/ocgcore/ocgHelper.ts
View file @
573f22bb
import
{
ygopro
}
from
"
./idl/ocgcore
"
;
import
{
ygopro
}
from
"
./idl/ocgcore
"
;
import
socketMiddleWare
,
{
socketCmd
}
from
"
../../middleware/socket
"
;
import
socketMiddleWare
,
{
socketCmd
}
from
"
../../middleware/socket
"
;
import
{
IDeck
}
from
"
../Card
"
;
import
{
IDeck
}
from
"
../Card
"
;
import
CtosPlayerInfo
from
"
./ocgAdapter/ctos/ctosPlayerInfo
"
;
import
PlayerInfoAdapter
from
"
./ocgAdapter/ctos/ctosPlayerInfo
"
;
import
CtosJoinGame
from
"
./ocgAdapter/ctos/ctosJoinGame
"
;
import
JoinGameAdapter
from
"
./ocgAdapter/ctos/ctosJoinGame
"
;
import
CtosUpdateDeck
from
"
./ocgAdapter/ctos/ctosUpdateDeck
"
;
import
UpdateDeckAdapter
from
"
./ocgAdapter/ctos/ctosUpdateDeck
"
;
import
CtosHsReady
from
"
./ocgAdapter/ctos/ctosHsReady
"
;
import
HsReadyAdapter
from
"
./ocgAdapter/ctos/ctosHsReady
"
;
import
CtosHsStart
from
"
./ocgAdapter/ctos/ctosHsStart
"
;
import
HsStartAdapter
from
"
./ocgAdapter/ctos/ctosHsStart
"
;
export
function
sendUpdateDeck
(
deck
:
IDeck
)
{
export
function
sendUpdateDeck
(
deck
:
IDeck
)
{
const
updateDeck
=
new
ygopro
.
YgoCtosMsg
({
const
updateDeck
=
new
ygopro
.
YgoCtosMsg
({
...
@@ -15,7 +15,9 @@ export function sendUpdateDeck(deck: IDeck) {
...
@@ -15,7 +15,9 @@ export function sendUpdateDeck(deck: IDeck) {
side
:
deck
.
side
,
side
:
deck
.
side
,
}),
}),
});
});
const
payload
=
new
CtosUpdateDeck
(
updateDeck
).
serialize
();
// 如果要实现UI层和Adapter层解耦,这里应该不感知具体Adapter类型
const
payload
=
new
UpdateDeckAdapter
(
updateDeck
).
serialize
();
socketMiddleWare
({
cmd
:
socketCmd
.
SEND
,
payload
});
socketMiddleWare
({
cmd
:
socketCmd
.
SEND
,
payload
});
}
}
...
@@ -24,7 +26,7 @@ export function sendHsReady() {
...
@@ -24,7 +26,7 @@ export function sendHsReady() {
const
hasReady
=
new
ygopro
.
YgoCtosMsg
({
const
hasReady
=
new
ygopro
.
YgoCtosMsg
({
ctos_hs_ready
:
new
ygopro
.
CtosHsReady
({}),
ctos_hs_ready
:
new
ygopro
.
CtosHsReady
({}),
});
});
const
payload
=
new
CtosHsReady
(
hasReady
).
serialize
();
const
payload
=
new
HsReadyAdapter
(
hasReady
).
serialize
();
socketMiddleWare
({
cmd
:
socketCmd
.
SEND
,
payload
});
socketMiddleWare
({
cmd
:
socketCmd
.
SEND
,
payload
});
}
}
...
@@ -33,7 +35,7 @@ export function sendHsStart() {
...
@@ -33,7 +35,7 @@ export function sendHsStart() {
const
hasStart
=
new
ygopro
.
YgoCtosMsg
({
const
hasStart
=
new
ygopro
.
YgoCtosMsg
({
ctos_hs_start
:
new
ygopro
.
CtosHsStart
({}),
ctos_hs_start
:
new
ygopro
.
CtosHsStart
({}),
});
});
const
payload
=
new
CtosHsStart
(
hasStart
).
serialize
();
const
payload
=
new
HsStartAdapter
(
hasStart
).
serialize
();
socketMiddleWare
({
cmd
:
socketCmd
.
SEND
,
payload
});
socketMiddleWare
({
cmd
:
socketCmd
.
SEND
,
payload
});
}
}
...
@@ -44,7 +46,7 @@ export function sendPlayerInfo(ws: WebSocket, player: string) {
...
@@ -44,7 +46,7 @@ export function sendPlayerInfo(ws: WebSocket, player: string) {
name
:
player
,
name
:
player
,
}),
}),
});
});
const
packet
=
new
CtosPlayerInfo
(
playerInfo
);
// todo: 需要收敛在一个层次里
const
packet
=
new
PlayerInfoAdapter
(
playerInfo
);
// todo: 需要收敛在一个层次里
ws
.
send
(
packet
.
serialize
());
ws
.
send
(
packet
.
serialize
());
}
}
...
@@ -57,7 +59,7 @@ export function sendJoinGame(ws: WebSocket, version: number, passWd: string) {
...
@@ -57,7 +59,7 @@ export function sendJoinGame(ws: WebSocket, version: number, passWd: string) {
passwd
:
passWd
,
passwd
:
passWd
,
}),
}),
});
});
const
packet
=
new
CtosJoinGame
(
joinGame
);
const
packet
=
new
JoinGameAdapter
(
joinGame
);
ws
.
send
(
packet
.
serialize
());
ws
.
send
(
packet
.
serialize
());
}
}
src/service/onSocketMessage.ts
View file @
573f22bb
import
{
ygopro
}
from
"
../api/ocgcore/idl/ocgcore
"
;
import
handleHsPlayerChange
from
"
./room/hsPlayerChange
"
;
import
handleHsPlayerChange
from
"
./room/hsPlayerChange
"
;
import
handleTypeChange
from
"
./room/typeChange
"
;
import
handleTypeChange
from
"
./room/typeChange
"
;
import
handleHsPlayerEnter
from
"
./room/hsPlayerEnter
"
;
import
handleHsPlayerEnter
from
"
./room/hsPlayerEnter
"
;
import
handleJoinGame
from
"
./room/joinGame
"
;
import
handleJoinGame
from
"
./room/joinGame
"
;
import
handleChat
from
"
./room/chat
"
;
import
handleChat
from
"
./room/chat
"
;
import
handleHsWatchChange
from
"
./room/hsWatchChange
"
;
import
handleHsWatchChange
from
"
./room/hsWatchChange
"
;
import
{
ygoArrayBuilder
}
from
"
../api/ocgcore/ocgAdapter/packet
"
;
import
{
ygoProPacket
}
from
"
../api/ocgcore/ocgAdapter/packet
"
;
import
StocJoinGame
from
"
../api/ocgcore/ocgAdapter/stoc/stocJoinGame
"
;
import
{
adaptStoc
}
from
"
../api/ocgcore/ocgAdapter/adapter
"
;
import
{
STOC_CHAT
,
STOC_HS_PLAYER_CHANGE
,
STOC_HS_PLAYER_ENTER
,
STOC_HS_WATCH_CHANGE
,
STOC_JOIN_GAME
,
STOC_TYPE_CHANGE
,
}
from
"
../api/ocgcore/ocgAdapter/protoDecl
"
;
import
StocChat
from
"
../api/ocgcore/ocgAdapter/stoc/stocChat
"
;
import
StocHsPlayerEnter
from
"
../api/ocgcore/ocgAdapter/stoc/stocHsPlayerEnter
"
;
import
StocHsPlayerChange
from
"
../api/ocgcore/ocgAdapter/stoc/stocHsPlayerChange
"
;
import
StocHsWatchChange
from
"
../api/ocgcore/ocgAdapter/stoc/stocHsWatchChange
"
;
import
StocTypeChange
from
"
../api/ocgcore/ocgAdapter/stoc/stocTypeChange
"
;
export
default
function
handleSocketMessage
(
e
:
MessageEvent
)
{
export
default
function
handleSocketMessage
(
e
:
MessageEvent
)
{
const
packet
=
new
ygoArrayBuilder
(
e
.
data
);
const
packet
=
ygoProPacket
.
deserialize
(
e
.
data
);
console
.
log
(
packet
);
const
pb
=
adaptStoc
(
packet
);
let
pb
=
new
ygopro
.
YgoStocMsg
({});
switch
(
packet
.
proto
)
{
case
STOC_JOIN_GAME
:
{
pb
=
new
StocJoinGame
(
packet
).
adapt
();
break
;
}
case
STOC_CHAT
:
{
pb
=
new
StocChat
(
packet
).
adapt
();
break
;
}
case
STOC_HS_PLAYER_ENTER
:
{
pb
=
new
StocHsPlayerEnter
(
packet
).
adapt
();
break
;
}
case
STOC_HS_PLAYER_CHANGE
:
{
pb
=
new
StocHsPlayerChange
(
packet
).
adapt
();
break
;
}
case
STOC_HS_WATCH_CHANGE
:
{
pb
=
new
StocHsWatchChange
(
packet
).
adapt
();
break
;
}
case
STOC_TYPE_CHANGE
:
{
pb
=
new
StocTypeChange
(
packet
).
adapt
();
break
;
}
default
:
{
break
;
}
}
switch
(
pb
.
msg
)
{
switch
(
pb
.
msg
)
{
case
"
stoc_join_game
"
:
{
case
"
stoc_join_game
"
:
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment