Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
957a980
fix the crash when create lobby
lmaosssdll Apr 1, 2026
248a0e3
fix the crash when create lobby
lmaosssdll Apr 1, 2026
961ce6e
uptade to new version
lmaosssdll Apr 1, 2026
998e2ea
fix the id for gd
lmaosssdll Apr 1, 2026
bf6040d
delete comments
lmaosssdll Apr 1, 2026
efb2cf2
delete comments
lmaosssdll Apr 1, 2026
a2398e0
Remove comment
lmaosssdll Apr 1, 2026
c9ab7bd
fix other crash error (desync server and client)
lmaosssdll Apr 1, 2026
a2f01cb
fix other crash error (desync server and client)
lmaosssdll Apr 1, 2026
854aae8
uptade chatpanel.cpp
lmaosssdll Apr 1, 2026
0fca948
Add m_currentPlayers vector to Lobby class
lmaosssdll Apr 1, 2026
210addd
add tags for lobby, notifications when someone leaves or joins, and w…
lmaosssdll Apr 1, 2026
2d86a9b
add better tags
lmaosssdll Apr 1, 2026
f465a86
add better tags
lmaosssdll Apr 1, 2026
c011621
add better tags
lmaosssdll Apr 1, 2026
1adc6ec
add better tags
lmaosssdll Apr 1, 2026
9d7b6c7
Fix memory leak
lmaosssdll Apr 3, 2026
1298ad2
Fix empty level and small memory leak
lmaosssdll Apr 3, 2026
537717b
Fix something
lmaosssdll Apr 3, 2026
e8ec9fa
Fix empty level
lmaosssdll Apr 3, 2026
60f0ab2
Delete tag logic:(
lmaosssdll Apr 3, 2026
dc9cf44
Add chat bubble and icon
lmaosssdll Apr 3, 2026
2d5a56c
Remove tag logic and debug
lmaosssdll Apr 3, 2026
a7303f1
Opss!
lmaosssdll Apr 3, 2026
5c19096
Opss!
lmaosssdll Apr 3, 2026
7e08e53
Opss!
lmaosssdll Apr 3, 2026
3ff7c8d
Update Lobby.cpp
lmaosssdll Apr 3, 2026
5e3cc29
My code is too bad
lmaosssdll Apr 3, 2026
4ae1526
Better chat
lmaosssdll Apr 3, 2026
66bbe6e
backup because i cant do a noraml bubbles
lmaosssdll Apr 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mod.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"geode": "5.3.0",
"geode": "5.5.1",
"gd": {
"win": "2.2081",
"android": "2.2081",
Expand Down
79 changes: 53 additions & 26 deletions server/src/types/swap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,54 +75,82 @@ export class Swap {
this.currentlySwapping = true
this.isSwapEnding = ending
this.closeReason = reason
log.debug(this.swapOrder)
log.debug(`[Swap] Turn ${this.currentTurn}/${this.totalTurns} started for lobby ${this.lobbyCode}. Swap order: ${this.swapOrder}`)
emitToLobby(this.serverState, this.lobbyCode, Packet.TimeToSwapPacket, {})
}

addLevel(level: LevelData, accId: number) {
if (!this.currentlySwapping) {
log.warn(`[Swap] Received late or duplicate level from ${accId} when not swapping. Ignoring.`)
return
}

const idx = this.swapOrder.indexOf(accId)
this.levels.push(
{
accountID: parseInt(offsetArray(this.swapOrder, 1)[idx]),
level
}
)
if (idx === -1) {
log.error(`[Swap] Player ${accId} is not in the swapOrder list!`)
return
}

const targetAccId = parseInt(offsetArray(this.swapOrder, 1)[idx] as string)

// FIX: Prevent duplicate submissions messing up the array length.
// If they already submitted, overwrite it. Otherwise, add it.
const existingIdx = this.levels.findIndex(l => l.accountID === targetAccId)

if (existingIdx !== -1) {
log.warn(`[Swap] Player ${accId} sent level data AGAIN. Overwriting previous submission.`)
this.levels[existingIdx] = { accountID: targetAccId, level }
} else {
this.levels.push({ accountID: targetAccId, level })
}

log.info(`[Swap] Received level from ${accId} (Size: ${level.levelString.length} bytes). Forwarding to player ${targetAccId}. (${this.levels.length}/${this.swapOrder.length})`)

this.checkSwap()
}

checkSwap() {
if (!this.currentlySwapping) return
this.lobby.accounts.forEach((acc, index) => {
if (this.serverState.lobbies[this.lobbyCode].accounts.findIndex(
lobbyAcc => lobbyAcc.accountID === acc.accountID
) !== -1) return

this.levels.splice(index, 1)
})
if (getLength(this.levels) < this.lobby.accounts.length) return

const currentLobbyAccounts = this.serverState.lobbies[this.lobbyCode].accounts

// FIX: Instead of checking array length (which breaks on duplicates or disconnects),
// we strictly check: Does EVERY player currently in the lobby have a level assigned to them?
let allReady = true
for (const acc of currentLobbyAccounts) {
const hasLevelForThisPlayer = this.levels.some(l => l.accountID === acc.accountID)
if (!hasLevelForThisPlayer) {
allReady = false
break
}
}

if (!allReady) {
// We are still waiting for someone to finish uploading their level
return
}

// Everyone has submitted their level! We can proceed.
this.currentlySwapping = false
log.info(`[Swap] All levels received for lobby ${this.lobbyCode}. Executing swap!`)

// Clean up: only send levels to players who are actually still in the lobby
const validLevels = this.levels.filter(l =>
currentLobbyAccounts.some(acc => acc.accountID === l.accountID)
)

if (!this.isSwapEnding) {
emitToLobby(this.serverState, this.lobbyCode, Packet.ReceiveSwappedLevelPacket, { levels: this.levels })
emitToLobby(this.serverState, this.lobbyCode, Packet.ReceiveSwappedLevelPacket, { levels: validLevels })

this.levels = []

if (this.currentTurn >= this.totalTurns) {
this.swapEnded = true
setTimeout(() => emitToLobby(this.serverState, this.lobbyCode, Packet.SwapEndedPacket, {}), 750) // 0.75 seconds

return
}
this.scheduleNextSwap()
}
// else {
// this.swapEnded = true
// this.levels = offsetArray(this.levels, this.totalTurns - this.currentTurn)
// emitToLobby(this.serverState, this.lobbyCode, Packet.ReceiveSwappedLevelPacket, { levels: this.levels })
// Object.values(this.serverState.sockets[this.lobbyCode]).forEach(socket => {
// socket.close(1000, this.closeReason)
// })
// }
}

scheduleNextSwap() {
Expand All @@ -136,4 +164,3 @@ export class Swap {
clearTimeout(this.timeout)
}
}

42 changes: 15 additions & 27 deletions src/layers/ChatPanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,17 @@ ChatPanel* ChatPanel::create() {
}

void ChatPanel::initialize() {
// initialize the listeners n' stuff if they havent already
if (!hasInitialized) {
auto& nm = NetworkManager::get();
nm.on<MessageSentPacket>([](MessageSentPacket packet) {
messages.push_back(packet.message);
messagesQueue.push_back(std::move(packet.message));
messagesQueue.push_back(packet.message);

// Уведомление о новом сообщении
Notification::create(
fmt::format("{} sent a message", packet.message.author.name),
CCSprite::createWithSpriteFrameName("GJ_chatBtn_001.png")
)->show();
});

hasInitialized = true;
Expand All @@ -31,7 +36,6 @@ bool ChatPanel::init() {
}

this->setTitle("Chat");

ChatPanel::initialize();

auto scrollContainer = CCScale9Sprite::create("square02b_001.png");
Expand All @@ -44,7 +48,6 @@ bool ChatPanel::init() {

scrollLayer = ScrollLayer::create(scrollContainer->getContentSize() - 10.f);
scrollLayer->ignoreAnchorPointForPosition(false);
// stolen from https://github.com/HJfod/LevelTrashcan/blob/main/src/TrashcanPopup.cpp#L41
scrollLayer->m_contentLayer->setLayout(
ColumnLayout::create()
->setAxisReverse(true)
Expand All @@ -53,19 +56,16 @@ bool ChatPanel::init() {
->setGap(0)
);
scrollContainer->addChildAtPosition(scrollLayer, Anchor::Center);

m_mainLayer->addChildAtPosition(scrollContainer, Anchor::Center, ccp(0, 5.f));

auto inputContainer = CCNode::create();
inputContainer->setContentSize({
scrollContainer->getContentWidth(),
75.f
});
inputContainer->setAnchorPoint({
0.5f, 0.f
});
inputContainer->setContentSize({ scrollContainer->getContentWidth(), 75.f });
inputContainer->setAnchorPoint({ 0.5f, 0.f });

messageInput = TextInput::create(inputContainer->getContentWidth(), "Send a message to the lobby!", "chatFont.fnt");

// --- ИСПРАВЛЕНИЕ: Разрешаем любые символы (кириллицу, спецсимволы) ---
messageInput->setCommonFilter(CommonFilter::Any);

auto sendMsgBtn = CCMenuItemExt::createSpriteExtraWithFrameName(
"GJ_chatBtn_001.png",
Expand All @@ -82,10 +82,7 @@ bool ChatPanel::init() {

inputContainer->addChild(messageInput);
inputContainer->addChild(msgBtnMenu);

inputContainer->setLayout(
RowLayout::create()
);
inputContainer->setLayout(RowLayout::create());
m_mainLayer->addChildAtPosition(inputContainer, Anchor::Bottom, ccp(0, 10.f));

for (auto message : ChatPanel::messages) {
Expand All @@ -101,22 +98,15 @@ void ChatPanel::renderMessage(Message const& message) {
auto msgNode = CCNode::create();
auto msgText = TextArea::create(
fmt::format("<cy>{}</c>: {}", message.author.name, message.message),
"chatFont.fnt",
0.5f,
scrollLayer->getContentWidth(),
{0.f, 0.f},
17.f,
false
"chatFont.fnt", 0.5f, scrollLayer->getContentWidth(), {0.f, 0.f}, 17.f, false
);
msgText->setAnchorPoint({ 0.f, 0.f });
msgNode->setContentHeight(msgText->m_label->m_lines->count() * 17.f);
msgNode->setContentWidth(scrollLayer->getContentWidth());
msgText->setPosition({ 0.f, 0.f });
msgNode->addChild(msgText);

scrollLayer->m_contentLayer->addChild(
msgNode
);
scrollLayer->m_contentLayer->addChild(msgNode);
scrollLayer->m_contentLayer->updateLayout();
}

Expand All @@ -129,7 +119,6 @@ void ChatPanel::updateMessages(float dt) {

void ChatPanel::clearMessages() {
messages.clear();

auto& nm = NetworkManager::get();
nm.unbind<MessageSentPacket>();
hasInitialized = false;
Expand All @@ -148,7 +137,6 @@ void ChatPanel::sendMessage() {

void ChatPanel::keyDown(cocos2d::enumKeyCodes keycode, double timestamp) {
if (keycode == cocos2d::KEY_Enter && CCIMEDispatcher::sharedDispatcher()->hasDelegate()) {
log::debug("sending via keybind");
sendMessage();
} else {
Popup::keyDown(keycode, timestamp);
Expand Down
Loading