Skip to content
This repository has been archived by the owner on Apr 5, 2022. It is now read-only.

Commit

Permalink
Fix Lua causing memory overrides by writing too many names to the ROM…
Browse files Browse the repository at this point in the history
…, port DeathLink handling from SuperNintendoClient
  • Loading branch information
LegendaryLinux committed Nov 24, 2021
1 parent 0005467 commit 36e9115
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 50 deletions.
164 changes: 116 additions & 48 deletions assets/serverSocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,39 @@ const CLIENT_STATUS = {

// DeathLink tracking
let deathLinkEnabled = false;
let lastForcedDeath = new Date().getTime();
let linkIsDead = false;
let linkIsStillDead = false;
let linkMustDie = false;
const DEATH_LINK_COOLDOWN = 3000; // Milliseconds
let lastDeathLink = new Date().getTime();
const DEATH_LINK_ALIVE = 0;
const DEATH_LINK_KILLING = 1;
const DEATH_LINK_DEAD = 2;
let deathLinkState = DEATH_LINK_ALIVE;

/**
* Returns a randomly chosen DeathLink message
* @param playerName
* @returns {string}
*/
const getRandomDeathLinkMessage = (playerName) => {
const deathLinkMessages = [
`${playerName} has died, and took you with them.`,
`${playerName} has met with a terrible fate, and they felt like sharing.`,
`${playerName} dug a grave big enough for everyone to share.`,
`Oh look, everyone died! Blame ${playerName}.`,
`Don't worry ${playerName}, nobody saw that. Because they're dead too.`,
`Have you ever heard the tragedy of Darth ${playerName} the wise?`,
`Death comes for us all. Because ${playerName} invited him.`,
`Death-warps aren't an option right now, ${playerName}...`,
`${playerName} used DeathLink! It's super-effective!`,
`Run for your lives! ${playerName} is killing people!`,
`Is ${playerName} throwing for content?`,
`${playerName} took an arrow to the knee. Now, their adventuring days are over.`,
`${playerName} has won a free trip to the title screen, and they invited some friends!`,
`All ${playerName}'s base are belong to us.`,
`It's dangerous to go alone, ${playerName}. Take everyone with you.`,
];

return deathLinkMessages[Math.floor(Math.random() * (deathLinkMessages.length))];
};

window.addEventListener('load', async () => {
// Handle server address change
Expand Down Expand Up @@ -277,50 +306,72 @@ const connectToServer = async (address, password=null) => {
sendLocationChecks(newLocationChecks);
}

// Determine if Link is currently dead
const linkIsAlive = await isLinkAlive();
if (linkIsAlive === null) {
appendConsoleMessage('Timeout while retrieving linkIsAlive.');
clearInterval(n64Interval);
n64IntervalComplete = true;
return;
}

// Useful boolean for logical purposes
const linkIsDead = (parseInt(linkIsAlive[0], 10) === 0);

// Check if DeathLink is enabled and Link is dead
if (deathLinkEnabled) {
if (linkIsDead) {
if (!linkIsStillDead) { // Link is dead, and it just happened
// Keep track of Link's state to prevent sending multiple DeathLink signals per death
linkIsStillDead = true;

// Check if it has been at least ten seconds since the last DeathLink network signal
// was send or received
if (new Date().getTime() > (lastForcedDeath + 10000)) {
if (serverStatus && serverSocket.readyState === WebSocket.OPEN) {
// Link just died, so ignore DeathLink signals for the next ten seconds
lastForcedDeath = new Date().getTime();
serverSocket.send(JSON.stringify([{
cmd: 'Bounce',
tags: ['DeathLink'],
data: {
time: Math.floor(lastForcedDeath / 1000),
source: players.find((player) =>
(player.team === playerTeam) && (player.slot === playerSlot)).alias, // Local player alias
},
}]));
}
if (
(deathLinkState === DEATH_LINK_ALIVE) && // Player was last known to be alive
((lastDeathLink + DEATH_LINK_COOLDOWN) < new Date().getTime()) // Cooldown has passed
) {
// Send the DeathLink message
if (serverSocket && serverSocket.readyState === WebSocket.OPEN) {
// Set the state to DEAD before sending the message
deathLinkState = DEATH_LINK_DEAD;

// Determine the DeathLink message
const causeMessage = getRandomDeathLinkMessage(players.find((player) =>
(player.team === playerTeam) && (player.slot === playerSlot)).alias);

// Send the DeathLink signal
lastDeathLink = new Date().getTime();
serverSocket.send(JSON.stringify([{
cmd: 'Bounce',
tags: ['DeathLink'],
data: {
time: (lastDeathLink / 1000),
source: players.find((player) =>
(player.team === playerTeam) && (player.slot === playerSlot)).alias,
cause: causeMessage,
}
}]));
appendConsoleMessage(causeMessage);
}
}
}

// If Link is supposed to die, kill him
if (linkMustDie) {
await killLink();
linkMustDie = false;
// If the player is dead, the DeathLink state must reflect that
deathLinkState = DEATH_LINK_DEAD;
}
}

// Determine if Link is currently dead
let linkIsAlive = await isLinkAlive();
if (linkIsAlive === null) {
appendConsoleMessage('Timeout while retrieving linkIsAlive.');
clearInterval(n64Interval);
n64IntervalComplete = true;
return;
} else {
linkIsDead = (parseInt(linkIsAlive[0], 10) === 0);
if (!linkIsDead) { linkIsStillDead = false; }
if (!linkIsDead) {
switch (deathLinkState) {
case DEATH_LINK_ALIVE:
// Do nothing, this is fine
break;

case DEATH_LINK_KILLING:
// Keep sending the kill signal if the player is supposed to be dead. This prevents bugs where
// sometimes players will end up with zero health, but still be alive
await killLink();
break;

case DEATH_LINK_DEAD:
// If the player is alive, DeathLink signals may be sent again
deathLinkState = DEATH_LINK_ALIVE;
break;
}
}
}

// Interval complete, allow a new run
Expand Down Expand Up @@ -409,15 +460,32 @@ const connectToServer = async (address, password=null) => {
break;

case 'Bounced':
// This command can be used for a variety of things. Currently, it is used for keep-alive and DeathLink.
// keep-alive packets can be safely ignored

// DeathLink handling
if (command.tags.includes('DeathLink')) {
// Has it been at least ten seconds since the last time Link was forcibly killed?
if (deathLinkEnabled && (new Date().getTime() > (lastForcedDeath + 10000))) {
// Notify the player of the DeathLink occurrence, and who is to blame
appendConsoleMessage(`${command.data.source} has died, and took you with them.`);

// Kill Link
linkMustDie = true;
if (
command.hasOwnProperty('tags') && // If there are tags on this message
command.tags.includes('DeathLink') && // If those tags include DeathLink
deathLinkEnabled // If DeathLink is enabled
) {
if (
(deathLinkState === DEATH_LINK_ALIVE) && // The player was last known to be alive
((lastDeathLink + DEATH_LINK_COOLDOWN) < new Date().getTime()) // Cooldown has passed
) {
// Update the DeathLink state and wait a split second
deathLinkState = DEATH_LINK_KILLING;
lastDeathLink = new Date().getTime();
await new Promise((resolve) => setTimeout(resolve, 50));

// Kill the player and print a message to the console informing the player of who is responsible
killLink().then(() => {
if (command.data.hasOwnProperty('cause') && command.data.cause) {
appendConsoleMessage(command.data.cause);
return;
}
appendConsoleMessage(getRandomDeathLinkMessage(command.data.source));
});
}
}
break;
Expand Down
5 changes: 3 additions & 2 deletions lua/ootMulti.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
local script_version = '2021-11-17' -- Should be the last modified date
local script_version = '2021-11-23' -- Should be the last modified date

--------------------------------------------------
-- Heavily modified form of RiptideSage's tracker
Expand Down Expand Up @@ -2430,10 +2430,11 @@ local runMessageWatcher = coroutine.wrap(function()
-- Returns message format: "requestComplete"
if command == 'setNames' then
local index = 2
while index <= #(messageParts) do
while ((index <= #(messageParts)) and (index < 510)) do
lib.setPlayerName(messageParts[index],messageParts[index+1])
index = index + 2 -- Increment twice each loop
end
lib.setPlayerName(510,'APServer')
connection:send('requestComplete')
return
end
Expand Down

0 comments on commit 36e9115

Please sign in to comment.