Skip to content

Commit

Permalink
Merge pull request #273 from Game-as-a-Service/feature/room-status
Browse files Browse the repository at this point in the history
feat: implement room features via Socket.IO
  • Loading branch information
Yuwen-ctw authored Aug 20, 2023
2 parents c7848db + 493eec2 commit 8b271ca
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 78 deletions.
2 changes: 1 addition & 1 deletion components/rooms/RoomBreadcrumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function RoomBreadcrumb({ roomInfo }: RoomBreadcrumbType) {
const isPublicText = (roomInfo.isLocked ? "非公開" : "公開") + "遊戲房間";
const maxPlayerText = `${roomInfo.maxPlayers}人房`;
const statusText =
roomInfo.status === "WATTING" ? "等待玩家中" : "遊戲進行中";
roomInfo.status === "WAITING" ? "等待玩家中" : "遊戲進行中";

const combinedText = roomInfo.name + "-" + maxPlayerText + "-" + statusText;
return (
Expand Down
8 changes: 8 additions & 0 deletions contexts/SocketContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ export enum SOCKET_EVENT {
CHATROOM_JOIN = "CHATROOM_JOIN",
CHATROOM_LEAVE = "CHATROOM_LEAVE",
CHAT_MESSAGE = "CHAT_MESSAGE",
USER_JOINED = "USER_JOINED",
USER_LEFT = "USER_LEFT",
USER_READY = "USER_READY",
USER_NOT_READY = "USER_NOT_READY",
GAME_STARTED = "GAME_STARTED",
GAME_ENDED = "GAME_ENDED",
HOST_CHANGED = "HOST_CHANGED",
ROOM_CLOSED = "ROOM_CLOSED",
}

const SocketContext = createContext<StoreContextType>({
Expand Down
25 changes: 14 additions & 11 deletions hooks/usePopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,23 @@ export default function usePopup() {
resetPopup();
}, [popupConfig, resetPopup]);

function firePopup({
title,
showCancelButton = false,
onConfirm,
onCancel,
}: FirePopupParamsType) {
setPopupConfig({
isOpen: true,
const firePopup = useCallback(
({
title,
showCancelButton,
showCancelButton = false,
onConfirm,
onCancel,
});
}
}: FirePopupParamsType) => {
setPopupConfig({
isOpen: true,
title,
showCancelButton,
onConfirm,
onCancel,
});
},
[]
);

const Popup = useMemo(() => {
function PopupComponent() {
Expand Down
41 changes: 25 additions & 16 deletions hooks/useRoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { RoomInfo } from "@/requests/rooms";
const initRoomInfo: RoomInfo.Room = {
id: "",
name: "string",
status: "WATTING",
status: "WAITING",
game: { id: "", name: "" },
host: { id: "", nickname: "", isReady: false },
isLocked: false,
Expand All @@ -20,7 +20,7 @@ const enum REDUCER_ACTION_TYPE {
REMOVE_PLAYER,
UPDATE_HOST,
UPDATE_ROOM_STATUS,
TOGGLE_USER_READY_STATUS,
UPDATE_USER_READY_STATUS,
CLEAN_UP_ROOM,
}

Expand Down Expand Up @@ -50,8 +50,8 @@ type UpdateRoomStatus = {
};

type UpdateUserReadyStatus = {
type: REDUCER_ACTION_TYPE.TOGGLE_USER_READY_STATUS;
payload: Pick<RoomInfo.User, "id">;
type: REDUCER_ACTION_TYPE.UPDATE_USER_READY_STATUS;
payload: Pick<RoomInfo.User, "id" | "isReady">;
};

type CleanUpRoomAction = {
Expand Down Expand Up @@ -109,17 +109,16 @@ const useRoomReducer = (
return { ...state, host: nextHost };
}

case REDUCER_ACTION_TYPE.TOGGLE_USER_READY_STATUS: {
case REDUCER_ACTION_TYPE.UPDATE_USER_READY_STATUS: {
const { payload } = action;
const nextPlayers = [...state.players];
const userIndex = nextPlayers.findIndex(
(player) => player.id === payload.id
);

if (userIndex === -1)
throw new Error("Recived invalid user id when toggling ready status.");
if (userIndex === -1) return state;

nextPlayers[userIndex].isReady = !nextPlayers[userIndex].isReady;
nextPlayers[userIndex].isReady = payload.isReady;
return {
...state,
players: nextPlayers,
Expand All @@ -128,7 +127,14 @@ const useRoomReducer = (

case REDUCER_ACTION_TYPE.UPDATE_ROOM_STATUS: {
const { payload } = action;
return { ...state, status: payload.status };
return {
...state,
status: payload.status,
players: state.players.map((player) => ({
...player,
isReady: payload.status === "PLAYING",
})),
};
}

case REDUCER_ACTION_TYPE.CLEAN_UP_ROOM: {
Expand Down Expand Up @@ -171,12 +177,15 @@ export default function useRoom() {
});
}, []);

const toggleUserReadyStatus = useCallback((userId: RoomInfo.User["id"]) => {
dispatch({
type: REDUCER_ACTION_TYPE.TOGGLE_USER_READY_STATUS,
payload: { id: userId },
});
}, []);
const updateUserReadyStatus = useCallback(
(payload: Omit<RoomInfo.User, "nickname">) => {
dispatch({
type: REDUCER_ACTION_TYPE.UPDATE_USER_READY_STATUS,
payload,
});
},
[]
);

const cleanUpRoom = useCallback(() => {
dispatch({ type: REDUCER_ACTION_TYPE.CLEAN_UP_ROOM });
Expand All @@ -189,7 +198,7 @@ export default function useRoom() {
removePlayer,
updateHost,
updateRoomStatus,
toggleUserReadyStatus,
updateUserReadyStatus,
cleanUpRoom,
};
}
2 changes: 1 addition & 1 deletion mocks/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const mock_createRoomResponse: Room = {
export const mock_roomInfo: RoomInfo.Room = {
id: "3345678",
name: "銀河路跑2v2",
status: "WATTING",
status: "WAITING",
game: { id: "456", name: "銀河路跑" },
host: {
id: "mock-currentUser-uid",
Expand Down
97 changes: 84 additions & 13 deletions pages/api/mock/socketio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,28 +37,99 @@ const socketio = async (req: NextApiRequest, res: NextApiResponseServerIO) => {
console.log("SOCKET CONNECTED IN SERVER! ", socket.id);
onlineUsers.set(socket.id, socket.id);

// avoid memory leak
if (!isEmitting) {
sendOnlineUsers = setInterval(
// eslint-disable-next-line no-console
() => console.log("Online users: ", onlineUsers),
5000
);
isEmitting = true;
}

socket.on(SOCKET_EVENT.CHAT_MESSAGE, (message) => {
// eslint-disable-next-line no-console
console.log("Message received in server: ", message);
switch (message.content) {
case SOCKET_EVENT.USER_JOINED:
socket.emit(SOCKET_EVENT.USER_JOINED, {
user: {
id: "mock_user_id",
nickname: "mock_user",
},
roomId: message.to,
});
break;
case SOCKET_EVENT.USER_LEFT:
socket.emit(SOCKET_EVENT.USER_LEFT, {
user: {
id: "mock_user_id",
nickname: "mock_user",
},
roomId: message.to,
});
break;
case "KICK_ME":
socket.emit(SOCKET_EVENT.USER_LEFT, {
user: {
id: "mock-currentUser-uid",
nickname: "mock currentUser",
},
roomId: message.to,
});
break;
case SOCKET_EVENT.USER_READY:
socket.emit(SOCKET_EVENT.USER_READY, {
user: {
id: "mock_user_id",
nickname: "mock_user",
},
roomId: message.to,
});
break;
case SOCKET_EVENT.USER_NOT_READY:
socket.emit(SOCKET_EVENT.USER_NOT_READY, {
user: {
id: "mock_user_id",
nickname: "mock_user",
},
roomId: message.to,
});
break;
case SOCKET_EVENT.HOST_CHANGED:
socket.emit(SOCKET_EVENT.HOST_CHANGED, {
user: {
id: "mock-currentUser-uid-c",
nickname: "mock user C",
},
roomId: message.to,
});
break;
case SOCKET_EVENT.GAME_STARTED:
socket.emit(SOCKET_EVENT.GAME_STARTED, {
gameUrl: "http://localhost:3030",
roomId: message.to,
});
break;
case SOCKET_EVENT.GAME_ENDED:
socket.emit(SOCKET_EVENT.GAME_ENDED, {
roomId: message.to,
});
break;
case SOCKET_EVENT.ROOM_CLOSED:
socket.emit(SOCKET_EVENT.ROOM_CLOSED, {
roomId: message.to,
});
break;
default:
break;
}
io.emit(SOCKET_EVENT.CHAT_MESSAGE, message);
});
socket.on(SOCKET_EVENT.DISCONNECT, () => {
onlineUsers.delete(socket.id);
// eslint-disable-next-line no-console
console.log("SOCKET DISCONNECTED IN SERVER! ", socket.id);
if (isEmitting && onlineUsers.size === 0) {
clearInterval(sendOnlineUsers);
isEmitting = false;
}
// avoid memory leak
if (!isEmitting) {
sendOnlineUsers = setInterval(
// eslint-disable-next-line no-console
() => console.log("Online users: ", onlineUsers),
5000
);
isEmitting = true;
}
});
});

Expand Down
Loading

0 comments on commit 8b271ca

Please sign in to comment.