diff --git a/src/app/game/berry-dash/chatroom/page.tsx b/src/app/game/berry-dash/chatroom/page.tsx new file mode 100644 index 0000000..6d5a1af --- /dev/null +++ b/src/app/game/berry-dash/chatroom/page.tsx @@ -0,0 +1,174 @@ +'use client' + +import { BackButton } from '@/app/components/BackButton' +import { DiscordButton } from '@/app/components/DiscordButton' +import { GetIconForUser } from '@/util/bd' +import axios from 'axios' +import { useEffect, useRef, useState } from 'react' + +interface WSMessage { + success: boolean + message: string | null + for: string + data: any +} + +type BirdColor = [number, number, number] + +interface Message { + username: string + userId: number + content: string + id: number + icon: number + overlay: number + birdColor: BirdColor + overlayColor: BirdColor + customIcon: string | null +} + +interface CustomIconEntry { + data: string + id: string +} + +export default function BerryDashChatroom () { + const [connected, setConnected] = useState(false) + const [messages, setMessages] = useState([]) + const [customIconCache, setCustomIconCache] = useState([]) + const [ws, setWs] = useState(null) + const messagesDiv = useRef(null) + + useEffect(() => { + document.title = 'Lncvrt Games - Chatroom' + + const socket = new WebSocket('wss://games.lncvrt.xyz/api/ws') + + socket.onopen = () => { + setConnected(true) + socket.send( + JSON.stringify({ + type: 'info_request', + kind: 'chatroom_messages', + timestamp: Date.now() + }) + ) + } + + socket.onmessage = event => { + const message = JSON.parse(event.data) as WSMessage + if (message.for == 'info_request:chatroom_messages') { + const messages = message.data as Message[] + setMessages(messages) + } else if (message.for == 'upload:chatroom_message') { + const msg = message.data as Message + setMessages(prev => [...prev.slice(1), msg]) + } + } + + socket.onclose = () => { + setConnected(false) + setMessages([]) + } + + setWs(socket) + + return () => { + socket.close() + } + }, []) + + useEffect(() => { + if (messagesDiv.current) { + messagesDiv.current.scrollTop = messagesDiv.current.scrollHeight + } + const all = customIconCache.map(icon => icon.id) + const allFromMessages = Array.from( + new Set( + messages + .filter(icon => icon.customIcon != null) + .map(icon => icon.customIcon as string) + ) + ) + const notInAllIds = allFromMessages + .filter(id => !all.includes(id)) + .map(id => `"${id}"`) + .join(',') + + ;(async () => { + const result = await axios.get( + `https://games.lncvrt.xyz/api/berrydash/icon-marketplace/icon?ids=[${notInAllIds}]` + ) + if (result.data.success) { + const add: CustomIconEntry[] = [] + for (const item of result.data.data) { + add.push({ + data: item.data, + id: item.id + }) + } + setCustomIconCache(prev => [ + ...prev, + ...result.data.data.map((item: CustomIconEntry) => ({ + data: item.data, + id: item.id + })) + ]) + } + })() + }, [messages]) + + return ( +
+ + +

+ Berry Dash Chatroom +

+ {connected && ( + <> +
+ {messages.map(item => { + return ( +
+ i.id === item.customIcon) + ? 'data:image/png;base64,' + + customIconCache.find(i => i.id === item.customIcon) + ?.data + : 'https://games-r2.lncvrt.xyz/game-assets/berrydash/other/loading.png' + } + className='pointer-events-none' + width={48} + height={48} + > +
+

{item.username}

+

+ {atob(item.content)} +

+
+
+ ) + })} +
+ + )} + {!connected && ( +

+ Not connected to the chatroom +

+ )} +
+ ) +} diff --git a/src/util/bd.ts b/src/util/bd.ts new file mode 100644 index 0000000..f0de39e --- /dev/null +++ b/src/util/bd.ts @@ -0,0 +1,7 @@ +export const GetIconForUser = (user: number) => { + if (user == 1) return -1 + else if (user == 2) return -2 + else if (user == 4) return -3 + else if (user == 3) return -4 + else return 1 +}