-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathio-handler.js
402 lines (299 loc) · 17 KB
/
io-handler.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
// 이 파일 앱과 웹 공동으로 사용되는 게임 서버 코드
// server_functions 에 함수 정의해서 여기에 import해서 써도 됨
// 이 곳은 클라이언트(앱, 웹)로부터 json 형식이나 문자열, 정수 등의 데이터를 받아와 게임 룰에 기반하여 데이처 처리 후
// 먄약 데이터를 보내준 클라이언트 한명에게만 답을 할꺼면 emit
// 모두에게 데이터를 공유할 경우 일단 api 사용이라고 표현해주면 됨
// ** 원래 io.to(room).emit 하면 해당 room에 있는 모든 사용자들한테 전송된다고 알고 있을 수도 있지만
// 실제는 이런식("http://graykick.tistory.com/5")으로 동작되기 때문에 위의 코드는 쓰지 말고 저 코드가 필요할 때는 api 사용이라고 표현 부탁
// 다만!! 하나의 프로세스만 이용해서 테스트할때는 io.to(room).emit 써도 되지만 어차피 다 없애야 되는 걸 염두하고 테스트용으로만 썼으면 좋겠다.( 미리 프론트 만들 용도로만 ?)
//===지금은 당장 꼭 엄청 중요한 얘기는 아니지만 참고용 =====
// 최종 생각하는 과정은 하나의 게임이 시작될 때 api를 통해 사용자들이 join할 room 단위로 redis에 channel도 같이 생성해주고 (물론 진행 중인 게임이 종료되면 channel도 바로 삭제)
// room에 join할 때는 api를 통해 redis의 특정 채널을 구독하도록 하고
// 데이터를 공유할 때는 api 파라미터로 데이터를 공유받아야될 대상과 데이터를 써주면 api내에서 pub.sub 매커니즘 기준으로
// publish를 해주어 구독한 클라이언트들에게만 데이터를 뿌려줄 수 있도록 생각만 해놓음
//가능하면 백엔드 모든 코드를 여기에 하지 말고 server_functions에 모듈화해서 여기는 최대한 간소화될 수 있게 부탁
// socket.io 대표 기능을 간단히 적어놓았고 게임 룰에 맞춰 응용하고 주의해야할 점은
// - firebase에 꼭 저장해야 할 정보인지
// - client와 통신하는 부분 즉, socket.io 쓰는 부분
// - client에서 데이터를 받아와 게임 룰에 따라 처리하는 부분은 크게 바뀔일이 없지만
// - 한명의 client가 자기와 함께 게임하는, 다른, client들 모두에게 데이터 공유 시는 api를 사용해야 하므로
// 지금 당장 테스트용 코드와 (//이부분 api요청) 설명을 꼭 같이 써주길 바람
// - 또한 room이름 고유하게! 끝나면 소속되어 있는 모든 클라이언트 leave되게! 쓸데없이 남아있거나 떠도는 room이 없게
// => 그래서 사용자가 leave를 해서 room에서 나가는 게 아니라 백엔드에서 퀴즈가 끝났다 싶으면 강제로 모든 클라이언트가 room에서 leave하도록 해야될 것 같음
// 해당 코드는 찾아보면 있는 것 같음 (ex) io.of(namespace).in(room).socketsleave(room))
// socket.io버전에 따라 코드가 조금씩 다르니 한번 찾아보길 바람
// 그리고 그곳에 api : redis channel 삭제나 모든 클라이언트 구독 취소 주석 같이 작성
// 주석 안해주면 내가 찾아서 할 때 너무 오래 걸릴 것 같아서 부탁해..ㅜㅜ
// api 빨리 만들껩...
// 일단 구조는 오늘 싹 다 갈아엎어서 기존 코드는 게임 서버 부분 일부 못쓰게 되서 일단 지금 구성도로 하면 웹 앱 공통으로 쓸 수 있는 서버 코드는 여기에 있고 웹 클라이언트 코드는 react로 있으며 앱 클라이언트 코드는 있다고 가정
// 안드로이드 클라이언트 코드는 좀 짜놨었는데 그거랑 상관없이 게임 서버 자체는 이론적으로 공용 서버 여야 하기 때문에 구조는 이렇게 해야할 것 같음 ( 서버에 클라이언트 코드가 아예 안들어가도록 )
// nodejs router 기능이 react dom router?기능으로로 가는 등 react로 클라이언트를 구현
// 마지막?으로 지금 socket.disconnect 안 넣어놨는데 클라이언트가 게임 서버와 통신이 이제 더 이상 안할 경우
// 해당 사이트에서 나갈 경우 socket이 disconnect 되도록만 해주면 될 것 같음
const url = require('url');
const func = require('./server_functions/db_func');
const async = require('async');
const { Socket } = require('dgram');
module.exports = (io) => {
var gameserver = io.of("dynamic-web_OXGame");
// let numUsers = 0; // 방 인원
// let users = []; // 방 인원 목록
// var playerList = [];
var rooms ={}; // 방별 관리 {numUsers ,users, manager} 저장
var roundChoice = []; // 라운드 별 선택한 답 {nickname: string, round: int, answer: string}
var rank = []; // 순위 닉네임 저장 {rank: int, nickname: string, count: int}
var outList = [];
gameserver.on('connection', (socket) => {
console.log("io-handler.js socket connect!!");
console.log("socketid : "+ socket.id);
// client마다 소켓 id가 다르다.
// 따라서 사용자 이름과 소켓 id를 해시값으로 저장해도 됨
const {ns} = url.parse(socket.handshake.url, true).query;
console.log(ns);
// =========== Waiting Room ===================
let addedUser = false; // added 유저 경우
// when the client emits 'add user', this listens and executes
socket.on('add user', (data) => {
console.log('[add user] add user 호출됨 addedUser : ', addedUser, 'user : ', data.nickname, 'manager : ', data.manager);
if (addedUser) return;
socket.nickname = data.nickname;
var room = data.room;
socket.room = data.room;
// 방 매니저가 아닌 경우에 rooms 리스트에 접속한 사용자 추가 (명수, 유저리스트)
if (data.nickname != data.manager)
{
++rooms[room].numUsers;
rooms[room].users.push(socket.nickname);
}
console.log("[add user *] : " + socket.nickname + " room : " + rooms[room]);
addedUser = true;
socket.join(room);
// Room 정보 전달
func.loadRoom(data.room).then(function (room){
console.log('[socket-loadRoom] room:',room);
socket.emit('loadRoom',room);
console.log('룸 정보 전송 완료');
});
// 사용자 로그인 알림
gameserver.in(room).emit('login', {
numUsers: rooms[room].numUsers,
users : rooms[room].users
});
// 새 사용자 입장 알림
gameserver.in(room).emit('user joined', {
nickname: socket.nickname,
numUsers: rooms[room].numUsers,
users : rooms[room].users
});
});
// WaitingRoom에서 관리자가 게임 시작 버튼을 눌렀을 경우
socket.on('game start', (data) => {
console.log('[game start] data: ', data);
func.updateRoom(data).then(function (result){
console.log('[socket-IsValidRoom-Then] result:',result);
gameserver.in(data.room).emit('game play');
});
})
// 사용자가 socket 접속을 끊었을 때 호출
socket.on('disconnect', (data) => {
console.log("[disconnected] : "+socket.nickname+" socket.room : "+socket.room);
if (addedUser) {
// 사용자가 방장이 아니라면 rooms 정보에서 해당 사용자 정보 뺌
if (socket.nickname != rooms[socket.room].manager){
-- rooms[socket.room].numUsers;
rooms[socket.room].users = rooms[socket.room].users.filter((user) => user !== socket.nickname);
}
console.log("!![disconnected] : "+socket.id+ " rooms : "+ rooms[socket.room]);
gameserver.in(socket.room).emit('user left', {
nickname: socket.nickname,
numUsers: rooms[socket.room].numUsers,
users : rooms[socket.room].users
});
addedUser = false;
socket.leave(socket.room);
}
});
// 모든 게임 종료 시(결과 페이지에서 home버튼 누를 때)에 game end
socket.on('game end', (data) => {
if (addedUser) {
// 사용자가 방 매니저가 아닌 경우만 room 정보 수정함
if (socket.nickname != data.manager){
-- rooms[socket.room].numUsers;
rooms[socket.room].users = rooms[socket.room].users.filter((user) => user !== socket.nickname);
console.log("잘 호출됨~!!~!");
}
// 추가 필요
console.log("!![disconnected] : "+socket.id+" num : "+rooms[socket.room]);
// gameserver.in(socket.room).emit('user left', {
// nickname: socket.nickname,
// numUsers: numUsers,
// users : users
// });
addedUser = false;
socket.leave(socket.room);
}
})
// =========== HOME ===================
// 홈 화면에서 해당 룸 (pin)이 활성화 되어 있는지 확인함
socket.on("isValidRoom", (room) => {
console.log('[socket-isValidRoom] room:',room);
func.IsValidRoom(room).then(function (data){
console.log('[socket-IsValidRoom-Then] permission:',data);
socket.emit('room permission',{
permission: data.permission,
manager : data.manager,
room: room
});
});
});
// ============ QUIZ INSERT ==================
socket.on("quiz", (data) => {
console.log("서버에서 quiz 수신함");
console.log(data);
console.log(data.problems[0]);
func.InsertQuiz(data);
});
// ========= CreateRoom =========
socket.on("loadQuiz", (data) =>{
console.log('[socket-loadQuiz] 호출됨');
func.loadQuiz(data.nickname).then(function (quizzes){
console.log('[socket-loadQuiz] quizzes:',quizzes);
socket.emit('showQuiz',quizzes);
console.log('추출된 퀴즈들 전송 완료');
});
});
socket.on("createRoom", (room) =>{
console.log('[socket-createRoom] 호출됨');
room['creationDate'] = nowDate();
room['roomPin'] = randomN();
func.InsertRoom(room);
rooms[room.roomPin] = {
numUsers : 0,
users : [],
manager : room.manager
}
console.log("[createRoom] rooms 딕셔너리 : " , rooms);
socket.emit('succesCreateRoom', {
roomPin: room.roomPin
});
});
// =========== EXTRACT QUIZ ===================
socket.on("nickname", (data) => {
console.log("서버에서 nickname 수신함(퀴즈 추출 위함)");
console.log('nickname: ', data);
func.ExtractQuiz(data).then(function (s_quizzes){
socket.emit('myQuizzes',s_quizzes);
console.log('추출된 퀴즈들 전송 완료');
});
});
// =========== DELETE QUIZ ===================
socket.on("drop_ID", (data) => {
console.log("삭제할 퀴즈 ID 수신", data);
func.DeleteQuiz(data);
});
// =========== Find QUIZ ===================
socket.on("get quiz", (data) => {
console.log("서버에서 퀴즈 추출해 전송");
console.log('io-handler find quiz : ', data);
roundChoice = [];
rank = [];
outList = [];
var playerNum = 0;
func.loadRoom(data.room).then(function (room_data){
console.log('get quiz - room data : ', room_data);
console.log('get quiz - room manager : ', room_data[0].manager);
playerList = room_data[0].players;
console.log("player list : ", playerList);
gameserver.in(data.room).emit('playerList', playerList);
playerNum = room_data[0].players_num;
limitedTime = room_data[0].limitedTime;
socket.emit('game_info', {playerNum: playerNum, limitedTime: limitedTime});
});
func.FindQuiz(data.room).then(function (quiz){
socket.emit('quiz', quiz);
console.log('findQuiz quiz : ', quiz);
console.log('추출된 퀴즈들 전송 완료');
});
});
// 답 선택 (닉네임으로 중복 제거를 하였는데 id가 더 좋을 수도 있을 것 같음)
socket.on("choiceAnswer", (data) => {
console.log("클라이언트에서 서버로 선택한 답 전송");
console.log("choiceAnswer 받은 데이터 : ", data);
console.log("choiceAnswer room id : ", data.room);
console.log("choiceAnswer 받은 데이터의 닉네임 : ", data.nickname);
console.log("시작 시 roundChoice : ", roundChoice);
const idx = roundChoice.findIndex(function(prev) {return prev.nickname === data.nickname});
console.log("삭제할 데이터 : ", idx);
if (idx != -1) {
console.log("데이터 삭제하기");
roundChoice.splice(idx, 1);
}
console.log("삭제 후 roundChoice : ", roundChoice);
roundChoice.push(data);
console.log("push까지 한 roundChoice : ", roundChoice);
gameserver.in(data.room).emit('usersChoice', roundChoice);
// socket.emit('usersChoice', roundChoice);
});
// 탈락한 인원 roundChoice에서 제외 시키기
socket.on("outQuiz", (data) => {
console.log("탈락한 사람의 데이터 : ", data);
console.log("탈락한 사람의 닉네임 : ", data.nickname);
rank.push(data);
outList.push(data.nickname);
// 데이터가 삭제 안 되어서 한 것임 (추후 삭제해도 됨 혹은 id로 구분하기)
var idx = roundChoice.findIndex(function(prev) {return prev.nickname === data.nickname});
if (idx != -1) {
console.log("데이터 삭제하기 : ", idx);
roundChoice.splice(idx, 1);
}
// 데이터가 삭제 안 되어서 한 것임 (추후 삭제해도 됨 혹은 id로 구분하기)
idx = playerList.findIndex(function(prev) {return prev === data.nickname});
if (idx != -1) {
console.log("데이터 추가하기 : ", idx);
playerList.splice(idx, 1);
}
console.log("삭제 후 리스트 : ", roundChoice);
gameserver.in(data.room).emit('usersChoice', roundChoice);
console.log("삭제 후 playerList 리스트 : ", playerList);
gameserver.in(data.room).emit('playerList', playerList);
console.log("삭제 후 outList 리스트 : ", outList);
gameserver.in(data.room).emit('outList', outList);
});
socket.on("endQuiz", (data) => {
console.log("퀴즈가 종료 됨 room id : ", data);
console.log("퀴즈 종료 랭크 : ", rank);
//현재 객체 배열을 정렬, count가 큰 객체부터
rank.sort(function (a, b) {
return a.count > b.count ? -1 : a.count < b.count ? 1 : 0;
});
console.log("정렬 후 rank : ", rank);
gameserver.in(data).emit('rankQuiz', rank);
});
})
function randomN(){
var randomNum = {};
//0~9까지의 난수
randomNum.random = function(n1, n2) {
return parseInt(Math.random() * (n2 -n1 +1)) + n1;
};
var value = "";
for(var i=0; i<5; i++){
value += randomNum.random(0,9);
}
return value;
};
function nowDate(){
var today = new Date();
var year = today.getFullYear();
var month = ('0' + (today.getMonth() + 1)).slice(-2);
var day = ('0' + today.getDate()).slice(-2);
var today = new Date();
var hours = ('0' + today.getHours()).slice(-2);
var minutes = ('0' + today.getMinutes()).slice(-2);
var seconds = ('0' + today.getSeconds()).slice(-2);
var dateString = year + '-' + month + '-' + day;
var timeString = hours + ':' + minutes + ':' + seconds;
var now_date = dateString + " " + timeString;
return now_date;
};
}