Toggle navigation
Toggle navigation
This project
Loading...
Sign in
강동현
/
nodejs-game
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Snippets
Network
Create a new issue
Builds
Commits
Issue Boards
Authored by
강동현
2021-06-02 00:50:29 +0900
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
eaadd6d24b3e582e2b785418b6c4dbacb225337e
eaadd6d2
1 parent
7105d386
게임 로직 작성
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
295 additions
and
9 deletions
PROTOCOL.md
common/dataType.ts
server/game/WordGuessingGame.ts
PROTOCOL.md
View file @
eaadd6d
...
...
@@ -68,6 +68,7 @@
이 정보들을 가지고 캔버스를 칠하는 컴포넌트를 만들어서, 이를
`drawer`
의 클라이언트에도 동일하게 사용하는 방식으로 구현하여
`drawer`
와 다른 플레이어의 캔버스가 동일하게 보이도록 해야 할 것입니다.
캔버스의 크기: 512x384 (4:3) (추후 변경 가능)
브러시 사이즈: 1 ~ 64px
### guesser
...
...
@@ -83,10 +84,12 @@
게임 도중 입장한 유저에게는 다음과 같은 메세지들이 모두 전송됩니다.
1.
준비 상태에서 자신이 방에 접속했을 때 전달 받는 모든 메세지들
2.
현재 라운드에 대한 정보를 담은
`startRound`
3.
현재 라운드 타이머와 동기화할 수 있는
`timer`
4.
마지막으로 서버상으로 기록된 브러시 정보를 담은
`setBrush`
5.
마지막으로 서버상으로 기록된 브러시 위치를 담은
`moveBrush`
2.
현재 라운드 타이머와 동기화할 수 있는
`timer`
3.
현재 라운드에 대한 정보를 담은
`startRound`
4.
현재 라운드가 종료되었고, 다음 라운드를 기다리고 있는 중이라면 이번 라운드의 답을 담은
`finishRound`
5.
마지막으로 서버상으로 기록된 브러시 정보를 담은
`setBrush`
6.
마지막으로 서버상으로 기록된 브러시 위치를 담은
`moveBrush`
// TODO: 중도 입장 유저에게는 비트맵을 전송하는 방식 고려해보기
다른 플레이어에게는 다음과 같은 메세지들이 모두 전송됩니다.
...
...
common/dataType.ts
View file @
eaadd6d
...
...
@@ -21,3 +21,5 @@ export interface RoomInfo {
export
interface
UserData
{
username
:
string
;
}
export
type
Role
=
"drawer"
|
"guesser"
|
"winner"
|
"spectator"
;
...
...
server/game/WordGuessingGame.ts
View file @
eaadd6d
import
{
roomChatHandler
}
from
"../message/handler/roomChatHandler"
;
import
{
Role
}
from
"../../common/dataType"
;
import
{
MessageHandler
}
from
"../message/MessageHandler"
;
import
{
Room
}
from
"../room/Room"
;
import
{
User
}
from
"../user/User"
;
import
{
Game
}
from
"./Game"
;
...
...
@@ -6,7 +7,37 @@ import { Game } from "./Game";
export
class
WorldGuessingGame
implements
Game
{
room
:
Room
;
maxRound
:
number
;
round
:
number
;
round
:
number
=
0
;
roundState
:
"choosing"
|
"running"
|
"done"
=
"choosing"
;
roundDuration
:
number
;
readonly
roundTerm
:
number
=
5
;
// 다음 라운드 시작까지 기다리는 시간
wordCandidates
:
string
[]
=
[];
word
:
string
=
""
;
timer
:
{
startTimeMillis
:
number
;
timeLeftMillis
:
number
;
running
:
boolean
;
}
=
{
startTimeMillis
:
0
,
timeLeftMillis
:
0
,
running
:
false
};
timeoutTimerId
?:
NodeJS
.
Timeout
;
nextRoundTimerId
?:
NodeJS
.
Timeout
;
brush
:
{
size
:
number
;
color
:
string
;
drawing
:
boolean
;
x
:
number
;
y
:
number
;
}
=
{
size
:
24
,
color
:
"000000"
,
drawing
:
false
,
x
:
0
,
y
:
0
,
};
handler
:
MessageHandler
;
roles
:
Map
<
User
,
Role
>
;
drawer
?:
User
;
constructor
(
room
:
Room
)
{
this
.
room
=
room
;
...
...
@@ -16,14 +47,264 @@ export class WorldGuessingGame implements Game {
// TODO: 방장이 설정
this
.
maxRound
=
5
;
this
.
round
=
1
;
this
.
roundDuration
=
60
;
this
.
handler
=
new
MessageHandler
({
chooseWord
:
(
user
,
message
)
=>
{
if
(
user
!==
this
.
drawer
||
this
.
roundState
===
"choosing"
)
{
return
{
ok
:
false
};
}
const
chosen
=
message
.
word
;
if
(
this
.
wordCandidates
.
includes
(
chosen
))
{
this
.
wordSelected
(
chosen
);
return
{
ok
:
true
};
}
return
{
ok
:
false
};
},
chat
:
(
user
,
message
)
=>
{
const
text
=
message
.
message
.
trim
();
if
(
this
.
roles
.
get
(
user
)
===
"guesser"
&&
text
===
this
.
word
)
{
this
.
acceptAnswer
(
user
);
}
else
{
this
.
room
.
sendChat
(
user
,
text
);
}
return
{
ok
:
true
};
},
setBrush
:
(
user
,
message
)
=>
{
if
(
user
!==
this
.
drawer
||
!
/^
[
0-9a-f
]{6}
$/
.
test
(
message
.
color
))
{
return
{
ok
:
false
};
}
this
.
brush
.
size
=
Math
.
max
(
Math
.
min
(
message
.
size
,
64
),
1
);
this
.
brush
.
color
=
message
.
color
;
this
.
brush
.
drawing
=
message
.
drawing
;
this
.
room
.
broadcast
(
"setBrush"
,
{
size
:
this
.
brush
.
size
,
color
:
this
.
brush
.
color
,
drawing
:
this
.
brush
.
drawing
,
},
user
);
return
{
ok
:
true
};
},
moveBrush
:
(
user
,
message
)
=>
{
if
(
user
!==
this
.
drawer
)
{
return
{
ok
:
false
};
}
this
.
brush
.
x
=
Math
.
max
(
Math
.
min
(
message
.
x
,
1
),
0
);
this
.
brush
.
y
=
Math
.
max
(
Math
.
min
(
message
.
y
,
1
),
0
);
this
.
room
.
broadcast
(
"moveBrush"
,
{
x
:
this
.
brush
.
x
,
y
:
this
.
brush
.
y
,
},
user
);
return
{
ok
:
true
};
},
});
this
.
roles
=
new
Map
<
User
,
Role
>
();
this
.
startNextRound
();
}
private
startNextRound
():
void
{
this
.
roundState
=
"choosing"
;
this
.
round
++
;
this
.
roles
.
clear
();
this
.
drawer
=
this
.
pickDrawer
();
this
.
room
.
users
.
forEach
((
user
)
=>
this
.
roles
.
set
(
user
,
"guesser"
));
this
.
roles
.
set
(
this
.
drawer
,
"drawer"
);
this
.
room
.
broadcast
(
"startRound"
,
{
round
:
this
.
round
,
duration
:
this
.
roundDuration
,
roles
:
this
.
makeRoleArray
(),
});
this
.
wordCandidates
=
this
.
pickWords
();
this
.
drawer
.
connection
.
send
(
"wordSet"
,
{
words
:
this
.
wordCandidates
});
}
private
wordSelected
(
word
:
string
):
void
{
this
.
word
=
word
;
this
.
roundState
=
"running"
;
this
.
room
.
broadcast
(
"wordChosen"
,
{
length
:
word
.
length
});
this
.
startTimer
(
this
.
roundDuration
*
1000
);
this
.
timeoutTimerId
=
setTimeout
(
this
.
finishRound
,
this
.
roundDuration
*
1000
);
}
private
finishRound
():
void
{
if
(
this
.
timeoutTimerId
)
{
clearTimeout
(
this
.
timeoutTimerId
);
this
.
timeoutTimerId
=
undefined
;
}
this
.
roundState
=
"done"
;
this
.
stopTimer
();
this
.
room
.
broadcast
(
"finishRound"
,
{
answer
:
this
.
word
});
this
.
prepareNextRound
();
}
private
prepareNextRound
():
void
{
this
.
nextRoundTimerId
=
setTimeout
(()
=>
{
if
(
this
.
round
==
this
.
maxRound
)
{
this
.
finishGame
();
}
else
{
this
.
startNextRound
();
}
},
this
.
roundTerm
*
1000
);
}
private
finishGame
():
void
{
this
.
room
.
broadcast
(
"finishGame"
,
{});
// TODO
}
private
forceFinishGame
()
{
if
(
this
.
timeoutTimerId
)
{
clearTimeout
(
this
.
timeoutTimerId
);
}
if
(
this
.
nextRoundTimerId
)
{
clearTimeout
(
this
.
nextRoundTimerId
);
}
this
.
room
.
broadcast
(
"finishRound"
,
{
answer
:
this
.
word
});
this
.
finishGame
();
}
private
acceptAnswer
(
user
:
User
):
void
{
user
.
connection
.
send
(
"answerAccepted"
,
{
answer
:
this
.
word
});
this
.
changeRole
(
user
,
"winner"
);
}
private
pickDrawer
():
User
{
const
candidates
=
this
.
room
.
users
.
filter
((
user
)
=>
user
!==
this
.
drawer
);
return
candidates
[
Math
.
floor
(
Math
.
random
()
*
candidates
.
length
)];
}
private
pickWords
():
string
[]
{
return
[
"장난감"
,
"백화점"
,
"파티"
];
}
private
startTimer
(
timeLeftMillis
:
number
):
void
{
this
.
timer
=
{
startTimeMillis
:
Date
.
now
(),
timeLeftMillis
,
running
:
true
,
};
}
private
stopTimer
():
void
{
this
.
timer
=
{
...
this
.
timer
,
running
:
false
,
};
this
.
room
.
users
.
forEach
((
user
)
=>
this
.
sendTimer
(
user
));
}
private
sendTimer
(
user
:
User
):
void
{
user
.
connection
.
send
(
"timer"
,
{
state
:
this
.
timer
.
running
?
"started"
:
"stopped"
,
time
:
Math
.
max
(
(
this
.
timer
.
startTimeMillis
+
this
.
timer
.
timeLeftMillis
-
Date
.
now
())
/
1000
,
0
),
});
}
private
makeRoleArray
():
{
username
:
string
;
role
:
Role
}[]
{
let
roleArray
:
{
username
:
string
;
role
:
Role
;
}[]
=
[];
this
.
roles
.
forEach
((
role
,
user
)
=>
roleArray
.
push
({
username
:
user
.
username
,
role
:
role
})
);
return
roleArray
;
}
private
changeRole
(
user
:
User
,
role
:
Role
)
{
this
.
roles
.
set
(
user
,
role
);
this
.
room
.
broadcast
(
"role"
,
{
username
:
user
.
username
,
role
});
}
join
(
user
:
User
):
void
{
throw
new
Error
(
"Method not implemented."
);
this
.
changeRole
(
user
,
"spectator"
);
this
.
sendTimer
(
user
);
user
.
connection
.
send
(
"startRound"
,
{
round
:
this
.
round
,
duration
:
this
.
roundDuration
,
roles
:
this
.
makeRoleArray
(),
});
if
(
this
.
roundState
===
"done"
)
{
user
.
connection
.
send
(
"finishRound"
,
{
answer
:
this
.
word
,
});
}
user
.
connection
.
send
(
"setBrush"
,
{
size
:
this
.
brush
.
size
,
color
:
this
.
brush
.
color
,
drawing
:
this
.
brush
.
drawing
,
});
user
.
connection
.
send
(
"moveBrush"
,
{
x
:
this
.
brush
.
x
,
y
:
this
.
brush
.
y
,
});
}
leave
(
user
:
User
):
void
{
throw
new
Error
(
"Method not implemented."
);
if
(
this
.
room
.
users
.
length
<
2
)
{
this
.
forceFinishGame
();
return
;
}
this
.
roles
.
delete
(
user
);
if
(
user
===
this
.
drawer
)
{
if
(
this
.
roundState
===
"choosing"
)
{
this
.
round
--
;
// 이번 라운드를 다시 시작
this
.
startNextRound
();
}
else
if
(
this
.
roundState
===
"running"
)
{
this
.
finishRound
();
}
}
else
{
let
guesserCount
=
0
;
this
.
roles
.
forEach
((
role
,
user
)
=>
{
if
(
role
===
"guesser"
)
{
guesserCount
++
;
}
});
if
(
guesserCount
<
1
)
{
if
(
this
.
roundState
===
"choosing"
)
{
this
.
round
--
;
this
.
startNextRound
();
}
else
if
(
this
.
roundState
===
"running"
)
{
this
.
finishRound
();
}
}
}
}
}
...
...
Please
register
or
login
to post a comment