158 lines
4.7 KiB
JavaScript
158 lines
4.7 KiB
JavaScript
|
|
const express = require('express')
|
||
|
|
const socketio = require('socket.io')
|
||
|
|
const http = require('http')
|
||
|
|
const cors = require('cors')
|
||
|
|
const { addUser, removeUser, getUser, getUsersInRoom } = require('./users')
|
||
|
|
const path = require('path')
|
||
|
|
|
||
|
|
const PORT = process.env.PORT || 5000
|
||
|
|
|
||
|
|
const app = express()
|
||
|
|
const server = http.createServer(app)
|
||
|
|
const io = socketio(server, {
|
||
|
|
cors: {
|
||
|
|
origin: "*",
|
||
|
|
methods: ["GET", "POST"]
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
app.use(cors())
|
||
|
|
|
||
|
|
// Store latest game states by room code for bot API access
|
||
|
|
const gameStates = new Map()
|
||
|
|
|
||
|
|
// HTTP endpoint for bot to get game state
|
||
|
|
app.get('/api/game/:roomCode/state', (req, res) => {
|
||
|
|
const roomCode = req.params.roomCode
|
||
|
|
const state = gameStates.get(roomCode)
|
||
|
|
|
||
|
|
if (state) {
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
gameState: state,
|
||
|
|
timestamp: new Date().toISOString()
|
||
|
|
})
|
||
|
|
} else {
|
||
|
|
res.status(404).json({
|
||
|
|
success: false,
|
||
|
|
error: 'Game room not found or no state available'
|
||
|
|
})
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
// HTTP endpoint for bot to submit action
|
||
|
|
app.post('/api/game/:roomCode/action', express.json(), (req, res) => {
|
||
|
|
const roomCode = req.params.roomCode
|
||
|
|
const action = req.body
|
||
|
|
|
||
|
|
// minimal log for incoming bot action
|
||
|
|
console.log(`[Bot HTTP Action] Room: ${roomCode}`)
|
||
|
|
|
||
|
|
// Emit action to the game room via socket
|
||
|
|
io.to(roomCode).emit('botActionReceived', action)
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
success: true,
|
||
|
|
message: 'Action received and forwarded to game'
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
io.on('connection', socket => {
|
||
|
|
socket.on('join', (payload, callback) => {
|
||
|
|
let numberOfUsersInRoom = getUsersInRoom(payload.room).length
|
||
|
|
|
||
|
|
const { error, newUser} = addUser({
|
||
|
|
id: socket.id,
|
||
|
|
name: numberOfUsersInRoom===0 ? 'Player 1' : 'Player 2',
|
||
|
|
room: payload.room
|
||
|
|
})
|
||
|
|
|
||
|
|
if(error)
|
||
|
|
return callback(error)
|
||
|
|
|
||
|
|
socket.join(newUser.room)
|
||
|
|
|
||
|
|
io.to(newUser.room).emit('roomData', {room: newUser.room, users: getUsersInRoom(newUser.room)})
|
||
|
|
socket.emit('currentUserData', {name: newUser.name})
|
||
|
|
callback()
|
||
|
|
})
|
||
|
|
|
||
|
|
socket.on('initGameState', gameState => {
|
||
|
|
const user = getUser(socket.id)
|
||
|
|
if(user)
|
||
|
|
io.to(user.room).emit('initGameState', gameState)
|
||
|
|
})
|
||
|
|
|
||
|
|
socket.on('updateGameState', gameState => {
|
||
|
|
const user = getUser(socket.id)
|
||
|
|
if(user) {
|
||
|
|
io.to(user.room).emit('updateGameState', gameState)
|
||
|
|
|
||
|
|
// Also update stored game state for bot REST API access
|
||
|
|
const currentState = gameStates.get(user.room) || {}
|
||
|
|
gameStates.set(user.room, {
|
||
|
|
...currentState,
|
||
|
|
...gameState,
|
||
|
|
room: user.room,
|
||
|
|
lastUpdate: new Date().toISOString()
|
||
|
|
})
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
socket.on('sendMessage', (payload, callback) => {
|
||
|
|
const user = getUser(socket.id)
|
||
|
|
io.to(user.room).emit('message', {user: user.name, text: payload.message})
|
||
|
|
callback()
|
||
|
|
})
|
||
|
|
|
||
|
|
// Bot integration: receive game state from Player 2 (bot)
|
||
|
|
socket.on('botGameState', gameState => {
|
||
|
|
const user = getUser(socket.id)
|
||
|
|
if(user && user.name === 'Player 2') {
|
||
|
|
// Store latest game state for bot access (do not log full JSON to avoid noise)
|
||
|
|
gameStates.set(user.room, {
|
||
|
|
...gameState,
|
||
|
|
room: user.room,
|
||
|
|
lastUpdate: new Date().toISOString()
|
||
|
|
})
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
// Bot integration: receive bot action (play card or draw)
|
||
|
|
socket.on('botAction', (action, callback) => {
|
||
|
|
const user = getUser(socket.id)
|
||
|
|
if(user && user.name === 'Player 2') {
|
||
|
|
// Forward action to game logic. Client will handle via regular game state updates
|
||
|
|
callback && callback({ success: true })
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
// Bot integration: request current game state
|
||
|
|
socket.on('requestGameState', (callback) => {
|
||
|
|
const user = getUser(socket.id)
|
||
|
|
if(user) {
|
||
|
|
// Request game state from room
|
||
|
|
socket.to(user.room).emit('gameStateRequested')
|
||
|
|
callback && callback({ success: true })
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
socket.on('disconnect', () => {
|
||
|
|
const user = removeUser(socket.id)
|
||
|
|
if(user)
|
||
|
|
io.to(user.room).emit('roomData', {room: user.room, users: getUsersInRoom(user.room)})
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
//serve static assets in production
|
||
|
|
if(process.env.NODE_ENV === 'production') {
|
||
|
|
//set static folder
|
||
|
|
app.use(express.static('client/build'))
|
||
|
|
app.get('*', (req, res) => {
|
||
|
|
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'))
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
server.listen(PORT, () => {
|
||
|
|
console.log(`Server running on port ${PORT}`)
|
||
|
|
})
|