Early version of multiplayer
This commit is contained in:
2 files changed
+327
-9
No files matched your search
@@ -3,7 +3,7 @@
|
||||
-- https://www.phpmyadmin.net/
|
||||
--
|
||||
-- Host: localhost
|
||||
-- Generation Time: Feb 21, 2026 at 09:07 PM
|
||||
-- Generation Time: Mar 05, 2026 at 06:35 PM
|
||||
-- Server version: 11.8.6-MariaDB-log
|
||||
-- PHP Version: 8.5.3
|
||||
|
||||
@@ -119,7 +119,7 @@ CREATE TABLE `userposts` (
|
||||
--
|
||||
ALTER TABLE `chatroom_reports`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD KEY `chatId` (`chatId`);
|
||||
ADD KEY `chatroom_reports?chatId` (`chatId`);
|
||||
|
||||
--
|
||||
-- Indexes for table `chats`
|
||||
@@ -199,7 +199,7 @@ ALTER TABLE `userposts`
|
||||
-- Constraints for table `chatroom_reports`
|
||||
--
|
||||
ALTER TABLE `chatroom_reports`
|
||||
ADD CONSTRAINT `chatId` FOREIGN KEY (`chatId`) REFERENCES `chats` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ADD CONSTRAINT `chatroom_reports?chatId` FOREIGN KEY (`chatId`) REFERENCES `chats` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
COMMIT;
|
||||
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
|
||||
+324
-6
@@ -7,6 +7,7 @@ import swagger from '@elysiajs/swagger'
|
||||
import { berryDashChats, berryDashUserData, users } from './lib/tables'
|
||||
import { and, desc, eq } from 'drizzle-orm'
|
||||
import { checkAuthorization } from './lib/auth'
|
||||
import { randomBytes } from 'crypto'
|
||||
|
||||
import { handler as getVerifyCodeHandler } from './routes/get-verify-code'
|
||||
|
||||
@@ -106,19 +107,315 @@ interface ClientInfoData {
|
||||
ClientPlatform: string
|
||||
}
|
||||
|
||||
interface MultiplayerClient {
|
||||
Id: string
|
||||
Key: string
|
||||
PosX: number
|
||||
PosY: number
|
||||
Flipped: boolean
|
||||
Paused: boolean
|
||||
FlaggedReason: string | null
|
||||
Token: string
|
||||
Username: string
|
||||
UserID: number
|
||||
Admin: boolean
|
||||
Platform: number | null
|
||||
JoinTimestamp: number
|
||||
}
|
||||
|
||||
interface MultiplayerBerry {
|
||||
Id: string
|
||||
Type: string
|
||||
Timestamp: number
|
||||
PosX: number
|
||||
}
|
||||
|
||||
const clients = new Set<ElysiaWS>()
|
||||
const clientInfo: ClientInfoData[] = []
|
||||
let clientInfo: ClientInfoData[] = []
|
||||
let multiplayerClients: MultiplayerClient[] = []
|
||||
let multiplayerBerries: MultiplayerBerry[] = []
|
||||
|
||||
setInterval(async () => {
|
||||
if (multiplayerClients.length == 0) return
|
||||
let berry = ''
|
||||
const spawnProbability = Math.random()
|
||||
const id = randomBytes(16).toString('hex')
|
||||
if (spawnProbability <= 0.575) {
|
||||
berry = 'NormalBerry'
|
||||
} else if (spawnProbability <= 0.75) {
|
||||
berry = 'PoisonBerry'
|
||||
} else if (spawnProbability <= 0.85) {
|
||||
berry = 'SlowBerry'
|
||||
} else if (spawnProbability <= 0.95) {
|
||||
berry = 'UltraBerry'
|
||||
} else {
|
||||
berry = 'RandomBerry'
|
||||
}
|
||||
const time = Date.now()
|
||||
const posX = Math.random()
|
||||
const data: MultiplayerBerry = {
|
||||
Id: id,
|
||||
Type: berry,
|
||||
Timestamp: time,
|
||||
PosX: posX
|
||||
}
|
||||
multiplayerBerries.push(data)
|
||||
|
||||
for (const multiplayerClientInfo of multiplayerClients) {
|
||||
const client = Array.from(clients).find(
|
||||
client => client.id === multiplayerClientInfo.Id
|
||||
)
|
||||
if (!client) continue
|
||||
client.send({
|
||||
type: 'multiplayer_berry_spawn',
|
||||
id,
|
||||
berryType: berry,
|
||||
posX,
|
||||
timestamp: time
|
||||
})
|
||||
}
|
||||
}, 1000)
|
||||
|
||||
app.ws('/ws', {
|
||||
maxPayloadLength: 10 * 1024 * 1024,
|
||||
open (ws) {
|
||||
clients.add(ws)
|
||||
console.log(ws.id, 'connected')
|
||||
// console.log(ws.id, 'connected')
|
||||
},
|
||||
async message (ws, message: any) {
|
||||
// console.log('received:', message, 'from', ws.id)
|
||||
console.log('received:', message, 'from', ws.id)
|
||||
if (message == 'ping') ws.send('ping')
|
||||
if (message.type) {
|
||||
if (message.type == 'connected_with_current_scene') {
|
||||
if (
|
||||
message.type == 'multiplayer_player_move' &&
|
||||
message.x !== undefined &&
|
||||
message.y !== undefined &&
|
||||
message.f !== undefined
|
||||
) {
|
||||
const multiplayerInfo = multiplayerClients.find(
|
||||
client => client.Id == ws.id
|
||||
)
|
||||
if (!multiplayerInfo) return
|
||||
multiplayerInfo.PosX = Number(message.x)
|
||||
multiplayerInfo.PosY = Number(message.y)
|
||||
multiplayerInfo.Flipped = Boolean(message.f)
|
||||
const index = multiplayerClients.findIndex(client => client.Id == ws.id)
|
||||
if (index === -1) return
|
||||
multiplayerClients[index] = multiplayerInfo
|
||||
for (const multiplayerClientInfo of multiplayerClients) {
|
||||
if (ws.id == multiplayerClientInfo.Id) continue
|
||||
const client = Array.from(clients).find(
|
||||
client => client.id === multiplayerClientInfo.Id
|
||||
)
|
||||
if (!client) continue
|
||||
client.send({
|
||||
type: 'multiplayer_player_move',
|
||||
id: multiplayerInfo.UserID,
|
||||
x: Number(message.x),
|
||||
y: Number(message.y),
|
||||
f: Boolean(message.f)
|
||||
})
|
||||
}
|
||||
} else if (message.type == 'multiplayer_leave') {
|
||||
const multiplayerInfo = multiplayerClients.find(
|
||||
client => client.Id == ws.id
|
||||
)
|
||||
if (!multiplayerInfo) return
|
||||
multiplayerClients = multiplayerClients.filter(
|
||||
client => client.Id !== ws.id
|
||||
)
|
||||
for (const multiplayerClientInfo of multiplayerClients) {
|
||||
if (ws.id == multiplayerClientInfo.Id) continue
|
||||
const client = Array.from(clients).find(
|
||||
client => client.id === multiplayerClientInfo.Id
|
||||
)
|
||||
if (!client) continue
|
||||
client.send({
|
||||
type: 'multiplayer_player_leave',
|
||||
id: multiplayerInfo.UserID
|
||||
})
|
||||
}
|
||||
} else if (message.type == 'multiplayer_join_request' && message.auth) {
|
||||
const dbInfo0 = getDatabaseConnection(0)
|
||||
const dbInfo1 = getDatabaseConnection(1)
|
||||
|
||||
if (!dbInfo0 || !dbInfo1) {
|
||||
ws.send({
|
||||
type: 'multiplayer_join_response',
|
||||
accepted: false,
|
||||
denyReason: 'Unable to authenticate',
|
||||
key: null,
|
||||
players: []
|
||||
})
|
||||
return
|
||||
}
|
||||
const { connection: connection0, db: db0 } = dbInfo0
|
||||
const { connection: connection1, db: db1 } = dbInfo1
|
||||
|
||||
const ip = ws.remoteAddress
|
||||
const authResult = await checkAuthorization(
|
||||
message.auth as string,
|
||||
db0,
|
||||
ip
|
||||
)
|
||||
if (!authResult.valid) {
|
||||
connection0.end()
|
||||
connection1.end()
|
||||
ws.send({
|
||||
type: 'multiplayer_join_response',
|
||||
accepted: false,
|
||||
denyReason: 'Unable to authenticate',
|
||||
id: null,
|
||||
players: []
|
||||
})
|
||||
return
|
||||
}
|
||||
const userId = authResult.id
|
||||
|
||||
const result = await db0
|
||||
.select({
|
||||
username: users.username,
|
||||
admin: users.admin,
|
||||
token: users.token
|
||||
})
|
||||
.from(users)
|
||||
.where(eq(users.id, userId))
|
||||
.execute()
|
||||
|
||||
if (result.length != 1) {
|
||||
connection0.end()
|
||||
connection1.end()
|
||||
ws.send({
|
||||
type: 'multiplayer_join_response',
|
||||
accepted: false,
|
||||
denyReason: 'Unable to authenticate',
|
||||
id: null,
|
||||
players: null
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const players: any = []
|
||||
for (const marketplaceInfo of multiplayerClients) {
|
||||
const userData = await db1
|
||||
.select({
|
||||
saveData: berryDashUserData.saveData
|
||||
})
|
||||
.from(berryDashUserData)
|
||||
.where(and(eq(berryDashUserData.id, marketplaceInfo.UserID)))
|
||||
.execute()
|
||||
|
||||
const savedata = userData[0].saveData
|
||||
? JSON.parse(userData[0].saveData)
|
||||
: null
|
||||
if (!savedata) {
|
||||
connection0.end()
|
||||
connection1.end()
|
||||
return
|
||||
}
|
||||
|
||||
const customIcon = savedata.bird?.customIcon?.selected ?? null
|
||||
const icon = customIcon ? 0 : savedata?.icon ?? 1
|
||||
const overlay = customIcon ? 1 : savedata?.overlay ?? 0
|
||||
const iconColor = customIcon
|
||||
? [255, 255, 255]
|
||||
: savedata?.settings?.colors?.icon ?? [255, 255, 255]
|
||||
const overlayColor = customIcon
|
||||
? [255, 255, 255]
|
||||
: savedata?.settings?.colors?.overlay ?? [255, 255, 255]
|
||||
|
||||
players.push({
|
||||
id: marketplaceInfo.UserID,
|
||||
username: result[0].username,
|
||||
admin: result[0].admin,
|
||||
timestamp: marketplaceInfo.JoinTimestamp,
|
||||
iconData: {
|
||||
icon,
|
||||
overlay,
|
||||
iconColor,
|
||||
overlayColor,
|
||||
customIcon: customIcon ?? ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const key = randomBytes(16).toString('hex')
|
||||
const time = Date.now()
|
||||
const data: MultiplayerClient = {
|
||||
Id: ws.id,
|
||||
Key: key,
|
||||
PosX: 0,
|
||||
PosY: -1,
|
||||
Flipped: false,
|
||||
Paused: false,
|
||||
FlaggedReason: null,
|
||||
Token: result[0].token, //Just to be safe
|
||||
Username: result[0].username,
|
||||
UserID: userId,
|
||||
Admin: result[0].admin,
|
||||
Platform: null,
|
||||
JoinTimestamp: time
|
||||
}
|
||||
multiplayerClients.push(data)
|
||||
ws.send({
|
||||
type: 'multiplayer_join_response',
|
||||
accepted: true,
|
||||
denyReason: null,
|
||||
players
|
||||
})
|
||||
|
||||
const userData = await db1
|
||||
.select({
|
||||
saveData: berryDashUserData.saveData
|
||||
})
|
||||
.from(berryDashUserData)
|
||||
.where(and(eq(berryDashUserData.id, userId)))
|
||||
.execute()
|
||||
|
||||
const savedata = userData[0].saveData
|
||||
? JSON.parse(userData[0].saveData)
|
||||
: null
|
||||
if (!savedata) {
|
||||
connection0.end()
|
||||
connection1.end()
|
||||
return
|
||||
}
|
||||
|
||||
const customIcon = savedata.bird?.customIcon?.selected ?? null
|
||||
const icon = customIcon ? 0 : savedata?.icon ?? 1
|
||||
const overlay = customIcon ? 1 : savedata?.overlay ?? 0
|
||||
const iconColor = customIcon
|
||||
? [255, 255, 255]
|
||||
: savedata?.settings?.colors?.icon ?? [255, 255, 255]
|
||||
const overlayColor = customIcon
|
||||
? [255, 255, 255]
|
||||
: savedata?.settings?.colors?.overlay ?? [255, 255, 255]
|
||||
|
||||
for (const multiplayerClientInfo of multiplayerClients) {
|
||||
if (ws.id == multiplayerClientInfo.Id) continue
|
||||
const client = Array.from(clients).find(
|
||||
client => client.id === multiplayerClientInfo.Id
|
||||
)
|
||||
if (!client) continue
|
||||
client.send({
|
||||
type: 'multiplayer_player_join',
|
||||
id: userId,
|
||||
username: result[0].username,
|
||||
admin: result[0].admin,
|
||||
timestamp: time,
|
||||
iconData: {
|
||||
icon,
|
||||
overlay,
|
||||
iconColor,
|
||||
overlayColor,
|
||||
customIcon: customIcon ?? ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
connection0.end()
|
||||
connection1.end()
|
||||
} else if (message.type == 'connected_with_current_scene') {
|
||||
if (
|
||||
!message.sceneName ||
|
||||
!message.Requester ||
|
||||
@@ -535,12 +832,33 @@ app.ws('/ws', {
|
||||
}
|
||||
}
|
||||
},
|
||||
close (ws) {
|
||||
async close (ws) {
|
||||
clients.forEach(client => {
|
||||
if (client.id === ws.id) clients.delete(client)
|
||||
})
|
||||
clientInfo = clientInfo.filter(client => client.Id !== ws.id)
|
||||
|
||||
console.log(ws.id, 'disconnected')
|
||||
const multiplayerInfo = multiplayerClients.find(
|
||||
client => client.Id == ws.id
|
||||
)
|
||||
if (multiplayerInfo) {
|
||||
multiplayerClients = multiplayerClients.filter(
|
||||
client => client.Id !== ws.id
|
||||
)
|
||||
for (const multiplayerClientInfo of multiplayerClients) {
|
||||
if (ws.id == multiplayerClientInfo.Id) continue
|
||||
const client = Array.from(clients).find(
|
||||
client => client.id === multiplayerClientInfo.Id
|
||||
)
|
||||
if (!client) continue
|
||||
client.send({
|
||||
type: 'multiplayer_player_leave',
|
||||
id: multiplayerInfo.UserID
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(ws.id, 'disconnected')
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in new issue
Block a user