add: absorb uno-online as regular subdirectory
UNO card game web app (Node.js/React) with Miku bot integration. Previously an independent git repo (fork of mizanxali/uno-online). Removed .git/ and absorbed into main repo for unified tracking. Includes bot integration code: botActionExecutor, cardParser, gameStateBuilder, and server-side bot action support. 37 files, node_modules excluded via local .gitignore.
This commit is contained in:
158
uno-online/server.js
Normal file
158
uno-online/server.js
Normal file
@@ -0,0 +1,158 @@
|
||||
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}`)
|
||||
})
|
||||
Reference in New Issue
Block a user