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/
|
-- https://www.phpmyadmin.net/
|
||||||
--
|
--
|
||||||
-- Host: localhost
|
-- 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
|
-- Server version: 11.8.6-MariaDB-log
|
||||||
-- PHP Version: 8.5.3
|
-- PHP Version: 8.5.3
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ CREATE TABLE `userposts` (
|
|||||||
--
|
--
|
||||||
ALTER TABLE `chatroom_reports`
|
ALTER TABLE `chatroom_reports`
|
||||||
ADD PRIMARY KEY (`id`),
|
ADD PRIMARY KEY (`id`),
|
||||||
ADD KEY `chatId` (`chatId`);
|
ADD KEY `chatroom_reports?chatId` (`chatId`);
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Indexes for table `chats`
|
-- Indexes for table `chats`
|
||||||
@@ -199,7 +199,7 @@ ALTER TABLE `userposts`
|
|||||||
-- Constraints for table `chatroom_reports`
|
-- Constraints for table `chatroom_reports`
|
||||||
--
|
--
|
||||||
ALTER 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;
|
COMMIT;
|
||||||
|
|
||||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
/*!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 { berryDashChats, berryDashUserData, users } from './lib/tables'
|
||||||
import { and, desc, eq } from 'drizzle-orm'
|
import { and, desc, eq } from 'drizzle-orm'
|
||||||
import { checkAuthorization } from './lib/auth'
|
import { checkAuthorization } from './lib/auth'
|
||||||
|
import { randomBytes } from 'crypto'
|
||||||
|
|
||||||
import { handler as getVerifyCodeHandler } from './routes/get-verify-code'
|
import { handler as getVerifyCodeHandler } from './routes/get-verify-code'
|
||||||
|
|
||||||
@@ -106,19 +107,315 @@ interface ClientInfoData {
|
|||||||
ClientPlatform: string
|
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 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', {
|
app.ws('/ws', {
|
||||||
maxPayloadLength: 10 * 1024 * 1024,
|
maxPayloadLength: 10 * 1024 * 1024,
|
||||||
open (ws) {
|
open (ws) {
|
||||||
clients.add(ws)
|
clients.add(ws)
|
||||||
console.log(ws.id, 'connected')
|
// console.log(ws.id, 'connected')
|
||||||
},
|
},
|
||||||
async message (ws, message: any) {
|
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) {
|
||||||
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 (
|
if (
|
||||||
!message.sceneName ||
|
!message.sceneName ||
|
||||||
!message.Requester ||
|
!message.Requester ||
|
||||||
@@ -535,12 +832,33 @@ app.ws('/ws', {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
close (ws) {
|
async close (ws) {
|
||||||
clients.forEach(client => {
|
clients.forEach(client => {
|
||||||
if (client.id === ws.id) clients.delete(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