mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2025-10-30 08:01:01 +00:00
673 lines
21 KiB
C
673 lines
21 KiB
C
#include "socket/socket.h"
|
|
#include <stdio.h>
|
|
#include "network.h"
|
|
#include "object_fields.h"
|
|
#include "object_constants.h"
|
|
#include "behavior_table.h"
|
|
#include "src/game/hardcoded.h"
|
|
#include "src/game/scroll_targets.h"
|
|
#ifdef DISCORD_SDK
|
|
#include "discord/discord.h"
|
|
#include "discord/activity.h"
|
|
#endif
|
|
#include "pc/configfile.h"
|
|
#include "pc/cheats.h"
|
|
#include "pc/djui/djui.h"
|
|
#include "pc/djui/djui_panel.h"
|
|
#include "pc/djui/djui_hud_utils.h"
|
|
#include "pc/djui/djui_panel_main.h"
|
|
#include "pc/utils/misc.h"
|
|
#include "pc/lua/smlua.h"
|
|
#include "pc/lua/utils/smlua_model_utils.h"
|
|
#include "pc/mods/mods.h"
|
|
#include "pc/crash_handler.h"
|
|
#include "pc/debuglog.h"
|
|
#include "game/camera.h"
|
|
#include "pc/gfx/gfx_pc.h"
|
|
#include "game/skybox.h"
|
|
#include "game/object_list_processor.h"
|
|
#include "game/object_helpers.h"
|
|
#include "game/level_geo.h"
|
|
#include "menu/intro_geo.h"
|
|
|
|
// fix warnings when including rendering_graph_node
|
|
#undef near
|
|
#undef far
|
|
#include "src/game/rendering_graph_node.h"
|
|
|
|
// Mario 64 specific externs
|
|
extern s16 sCurrPlayMode;
|
|
extern s16 gCurrCourseNum, gCurrActStarNum, gCurrLevelNum, gCurrAreaIndex;
|
|
|
|
enum NetworkType gNetworkType = NT_NONE;
|
|
#ifdef DISCORD_SDK
|
|
struct NetworkSystem* gNetworkSystem = &gNetworkSystemDiscord;
|
|
#else
|
|
struct NetworkSystem* gNetworkSystem = &gNetworkSystemSocket;
|
|
#endif
|
|
|
|
#define LOADING_LEVEL_THRESHOLD 10
|
|
#define MAX_PACKETS_PER_SECOND_PER_PLAYER ((u16)100)
|
|
|
|
u16 networkLoadingLevel = 0;
|
|
bool gNetworkAreaLoaded = false;
|
|
bool gNetworkAreaSyncing = true;
|
|
u32 gNetworkAreaTimerClock = 0;
|
|
u32 gNetworkAreaTimer = 0;
|
|
void* gNetworkServerAddr = NULL;
|
|
bool gNetworkSentJoin = false;
|
|
u16 gNetworkRequestLocationTimer = 0;
|
|
|
|
u8 gDebugPacketIdBuffer[256] = { 0xFF };
|
|
u8 gDebugPacketSentBuffer[256] = { 0 };
|
|
u8 gDebugPacketOnBuffer = 0;
|
|
|
|
u32 gNetworkStartupTimer = 0;
|
|
u32 sNetworkReconnectTimer = 0;
|
|
u32 sNetworkRehostTimer = 0;
|
|
enum NetworkSystemType sNetworkReconnectType = NS_SOCKET;
|
|
|
|
struct StringLinkedList gRegisteredMods = { 0 };
|
|
|
|
struct ServerSettings gServerSettings = {
|
|
.playerInteractions = PLAYER_INTERACTIONS_SOLID,
|
|
.playerKnockbackStrength = 25,
|
|
.skipIntro = 0,
|
|
.shareLives = 0,
|
|
.enableCheats = 0,
|
|
.bubbleDeath = 1,
|
|
.enablePlayersInLevelDisplay = 1,
|
|
.enablePlayerList = 1,
|
|
.headlessServer = 0,
|
|
};
|
|
|
|
void network_set_system(enum NetworkSystemType nsType) {
|
|
network_forget_all_reliable();
|
|
switch (nsType) {
|
|
case NS_SOCKET: gNetworkSystem = &gNetworkSystemSocket; break;
|
|
#ifdef DISCORD_SDK
|
|
case NS_DISCORD: gNetworkSystem = &gNetworkSystemDiscord; break;
|
|
#endif
|
|
default: LOG_ERROR("Unknown network system: %d", nsType);
|
|
}
|
|
}
|
|
|
|
bool network_init(enum NetworkType inNetworkType) {
|
|
// reset override hide hud
|
|
extern u8 gOverrideHideHud;
|
|
gOverrideHideHud = 0;
|
|
gNetworkStartupTimer = 5 * 30;
|
|
|
|
// sanity check network system
|
|
if (gNetworkSystem == NULL) {
|
|
LOG_ERROR("no network system attached");
|
|
return false;
|
|
}
|
|
|
|
network_forget_all_reliable();
|
|
crash_handler_init();
|
|
|
|
// set server settings
|
|
gServerSettings.playerInteractions = configPlayerInteraction;
|
|
gServerSettings.playerKnockbackStrength = configPlayerKnockbackStrength;
|
|
gServerSettings.stayInLevelAfterStar = configStayInLevelAfterStar;
|
|
gServerSettings.skipIntro = configSkipIntro;
|
|
gServerSettings.shareLives = configShareLives;
|
|
gServerSettings.enableCheats = configEnableCheats;
|
|
gServerSettings.bubbleDeath = configBubbleDeath;
|
|
#if defined(RAPI_DUMMY) || defined(WAPI_DUMMY)
|
|
gServerSettings.headlessServer = (inNetworkType == NT_SERVER);
|
|
#else
|
|
gServerSettings.headlessServer = 0;
|
|
#endif
|
|
|
|
// initialize the network system
|
|
gNetworkSentJoin = false;
|
|
int rc = gNetworkSystem->initialize(inNetworkType);
|
|
if (!rc) {
|
|
LOG_ERROR("failed to initialize network system");
|
|
return false;
|
|
}
|
|
if (gNetworkServerAddr != NULL) {
|
|
free(gNetworkServerAddr);
|
|
gNetworkServerAddr = NULL;
|
|
}
|
|
|
|
// set network type
|
|
gNetworkType = inNetworkType;
|
|
|
|
if (gNetworkType == NT_SERVER) {
|
|
extern s16 gCurrSaveFileNum;
|
|
gCurrSaveFileNum = configHostSaveSlot;
|
|
|
|
mods_activate(&gLocalMods);
|
|
smlua_init();
|
|
|
|
dynos_behavior_hook_all_custom_behaviors();
|
|
|
|
network_player_connected(NPT_LOCAL, 0, configPlayerModel, &configPlayerPalette, configPlayerName);
|
|
extern u8* gOverrideEeprom;
|
|
gOverrideEeprom = NULL;
|
|
|
|
if (gCurrLevelNum != (s16)gLevelValues.entryLevel) {
|
|
extern s16 gChangeLevelTransition;
|
|
gChangeLevelTransition = gLevelValues.entryLevel;
|
|
}
|
|
|
|
djui_chat_box_create();
|
|
}
|
|
|
|
configfile_save(configfile_name());
|
|
|
|
LOG_INFO("initialized");
|
|
|
|
return true;
|
|
}
|
|
|
|
void network_on_init_area(void) {
|
|
// reset loading timer
|
|
networkLoadingLevel = 0;
|
|
gNetworkAreaLoaded = false;
|
|
gNetworkAreaSyncing = true;
|
|
gNetworkAreaTimer = 0;
|
|
gNetworkAreaTimerClock = clock_elapsed_ticks();
|
|
}
|
|
|
|
void network_on_loaded_area(void) {
|
|
area_remove_sync_ids_clear();
|
|
struct NetworkPlayer* np = gNetworkPlayerLocal;
|
|
if (np != NULL) {
|
|
bool levelMatch = (np->currCourseNum == gCurrCourseNum
|
|
&& np->currActNum == gCurrActStarNum
|
|
&& np->currLevelNum == gCurrLevelNum);
|
|
if (np->currLevelSyncValid && levelMatch && np->currAreaIndex != gCurrAreaIndex) {
|
|
network_send_change_area();
|
|
} else {
|
|
network_send_change_level();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void network_remember_debug_packet(u8 id, bool sent) {
|
|
if (id == PACKET_ACK) { return; }
|
|
if (id == PACKET_KEEP_ALIVE) { return; }
|
|
if (id == PACKET_DEBUG_SYNC) { return; }
|
|
if (id == PACKET_PLAYER && id == gDebugPacketIdBuffer[gDebugPacketOnBuffer]) { return; }
|
|
if (id == PACKET_OBJECT && id == gDebugPacketIdBuffer[gDebugPacketOnBuffer]) { return; }
|
|
gDebugPacketOnBuffer++;
|
|
gDebugPacketIdBuffer[gDebugPacketOnBuffer] = id;
|
|
gDebugPacketSentBuffer[gDebugPacketOnBuffer] = sent;
|
|
}
|
|
|
|
bool network_allow_unknown_local_index(enum PacketType packetType) {
|
|
return (packetType == PACKET_JOIN_REQUEST)
|
|
|| (packetType == PACKET_KICK)
|
|
|| (packetType == PACKET_ACK)
|
|
|| (packetType == PACKET_MOD_LIST_REQUEST)
|
|
|| (packetType == PACKET_MOD_LIST)
|
|
|| (packetType == PACKET_MOD_LIST_ENTRY)
|
|
|| (packetType == PACKET_MOD_LIST_FILE)
|
|
|| (packetType == PACKET_MOD_LIST_DONE)
|
|
|| (packetType == PACKET_DOWNLOAD_REQUEST)
|
|
|| (packetType == PACKET_DOWNLOAD)
|
|
|| (packetType == PACKET_KEEP_ALIVE)
|
|
|| (packetType == PACKET_DEBUG_SYNC)
|
|
|| (packetType == PACKET_PING)
|
|
|| (packetType == PACKET_PONG);
|
|
}
|
|
|
|
void network_send_to(u8 localIndex, struct Packet* p) {
|
|
if (p == NULL) {
|
|
LOG_ERROR("no data to send");
|
|
return;
|
|
}
|
|
|
|
// set destination
|
|
if (localIndex == PACKET_DESTINATION_SERVER) {
|
|
packet_set_destination(p, 0);
|
|
localIndex = (gNetworkPlayerServer != NULL) ? gNetworkPlayerServer->localIndex : 0;
|
|
} else {
|
|
packet_set_destination(p, p->requestBroadcast
|
|
? PACKET_DESTINATION_BROADCAST
|
|
: gNetworkPlayers[(localIndex == 0) ? p->localIndex : localIndex].globalIndex);
|
|
}
|
|
|
|
// sanity checks
|
|
if (gNetworkType == NT_NONE) { LOG_ERROR("network type error none!"); return; }
|
|
if (p->error) { LOG_ERROR("packet error!"); return; }
|
|
if (gNetworkSystem == NULL) { LOG_ERROR("no network system attached"); return; }
|
|
if (localIndex == 0 && !network_allow_unknown_local_index(p->buffer[0])) {
|
|
LOG_ERROR("\n####################\nsending to myself, packetType: %d\n####################\n", p->packetType);
|
|
// SOFT_ASSERT(false); - Crash?
|
|
return;
|
|
}
|
|
|
|
if (gNetworkType == NT_SERVER) {
|
|
struct NetworkPlayer* np = &gNetworkPlayers[localIndex];
|
|
// don't send a packet to a player that can't receive it
|
|
if (p->levelAreaMustMatch) {
|
|
if (p->courseNum != np->currCourseNum) { return; }
|
|
if (p->actNum != np->currActNum) { return; }
|
|
if (p->levelNum != np->currLevelNum) { return; }
|
|
if (p->areaIndex != np->currAreaIndex) { return; }
|
|
} else if (p->levelMustMatch) {
|
|
if (p->courseNum != np->currCourseNum) { return; }
|
|
if (p->actNum != np->currActNum) { return; }
|
|
if (p->levelNum != np->currLevelNum) { return; }
|
|
}
|
|
}
|
|
|
|
// set the flags again
|
|
packet_set_flags(p);
|
|
|
|
p->localIndex = localIndex;
|
|
|
|
// set ordered data (MUST BE IMMEDITAELY BEFORE network_remember_reliable())
|
|
if (p->orderedGroupId != 0 && !p->sent) {
|
|
packet_set_ordered_data(p);
|
|
}
|
|
|
|
// remember reliable packets
|
|
network_remember_reliable(p);
|
|
|
|
// save inside packet buffer
|
|
u32 hash = packet_hash(p);
|
|
memcpy(&p->buffer[p->dataLength], &hash, sizeof(u32));
|
|
|
|
// redirect to server if required
|
|
if (localIndex != 0 && gNetworkType != NT_SERVER && gNetworkSystem->requireServerBroadcast && gNetworkPlayerServer != NULL) {
|
|
localIndex = gNetworkPlayerServer->localIndex;
|
|
}
|
|
|
|
SOFT_ASSERT(p->dataLength < PACKET_LENGTH);
|
|
|
|
// rate limit packets
|
|
bool tooManyPackets = false;
|
|
s32 maxPacketsPerSecond = (gNetworkType == NT_SERVER) ? (MAX_PACKETS_PER_SECOND_PER_PLAYER * (u16)network_player_connected_count()) : MAX_PACKETS_PER_SECOND_PER_PLAYER;
|
|
static s32 sPacketsPerSecond[MAX_PLAYERS] = { 0 };
|
|
static f32 sPacketsPerSecondTime[MAX_PLAYERS] = { 0 };
|
|
f32 currentTime = clock_elapsed();
|
|
if ((currentTime - sPacketsPerSecondTime[localIndex]) > 0) {
|
|
if (sPacketsPerSecond[localIndex] > maxPacketsPerSecond) {
|
|
LOG_ERROR("Too many packets sent to localIndex %d! Attempted %d. Connected count %d.", localIndex, sPacketsPerSecond[localIndex], network_player_connected_count());
|
|
}
|
|
sPacketsPerSecondTime[localIndex] = currentTime;
|
|
sPacketsPerSecond[localIndex] = 1;
|
|
} else {
|
|
sPacketsPerSecond[localIndex]++;
|
|
if (sPacketsPerSecond[localIndex] > maxPacketsPerSecond) {
|
|
tooManyPackets = true;
|
|
}
|
|
}
|
|
|
|
// send
|
|
if (!tooManyPackets) {
|
|
if (p->keepSendingAfterDisconnect) {
|
|
localIndex = 0; // Force this type of packet to use the saved addr
|
|
}
|
|
int rc = gNetworkSystem->send(localIndex, p->addr, p->buffer, p->cursor + sizeof(u32));
|
|
if (rc == SOCKET_ERROR) { LOG_ERROR("send error %d", rc); return; }
|
|
}
|
|
p->sent = true;
|
|
|
|
network_remember_debug_packet(p->packetType, true);
|
|
|
|
gNetworkPlayers[localIndex].lastSent = clock_elapsed();
|
|
}
|
|
|
|
void network_send(struct Packet* p) {
|
|
if (p == NULL) {
|
|
LOG_ERROR("no data to send");
|
|
return;
|
|
}
|
|
// prevent errors during writing from propagating
|
|
if (p->writeError) {
|
|
LOG_ERROR("packet has write error: %u", p->packetType);
|
|
return;
|
|
}
|
|
|
|
// set the flags again
|
|
packet_set_flags(p);
|
|
|
|
if (gNetworkType != NT_SERVER) {
|
|
p->requestBroadcast = TRUE;
|
|
if (gNetworkSystem != NULL && gNetworkSystem->requireServerBroadcast && gNetworkPlayerServer != NULL) {
|
|
int i = gNetworkPlayerServer->localIndex;
|
|
p->localIndex = i;
|
|
p->sent = false;
|
|
network_send_to(i, p);
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (s32 i = 1; i < MAX_PLAYERS; i++) {
|
|
struct NetworkPlayer* np = &gNetworkPlayers[i];
|
|
if (!np->connected) { continue; }
|
|
|
|
// don't send a packet to a player that can't receive it
|
|
if (p->levelAreaMustMatch) {
|
|
if (p->courseNum != np->currCourseNum) { continue; }
|
|
if (p->actNum != np->currActNum) { continue; }
|
|
if (p->levelNum != np->currLevelNum) { continue; }
|
|
if (p->areaIndex != np->currAreaIndex) { continue; }
|
|
} else if (p->levelMustMatch) {
|
|
if (p->courseNum != np->currCourseNum) { continue; }
|
|
if (p->actNum != np->currActNum) { continue; }
|
|
if (p->levelNum != np->currLevelNum) { continue; }
|
|
}
|
|
|
|
p->localIndex = i;
|
|
p->sent = false;
|
|
network_send_to(i, p);
|
|
}
|
|
}
|
|
|
|
void network_receive(u8 localIndex, void* addr, u8* data, u16 dataLength) {
|
|
// receive packet
|
|
struct Packet p = {
|
|
.localIndex = localIndex,
|
|
.cursor = 3,
|
|
.addr = addr,
|
|
.buffer = { 0 },
|
|
.dataLength = dataLength,
|
|
};
|
|
memcpy(p.buffer, data, dataLength);
|
|
|
|
if (localIndex != UNKNOWN_LOCAL_INDEX && localIndex != 0) {
|
|
gNetworkPlayers[localIndex].lastReceived = clock_elapsed();
|
|
}
|
|
|
|
// subtract and check hash
|
|
p.dataLength -= sizeof(u32);
|
|
if (!packet_check_hash(&p)) {
|
|
LOG_ERROR("invalid packet hash!");
|
|
return;
|
|
}
|
|
|
|
network_remember_debug_packet(p.buffer[0], false);
|
|
|
|
// execute packet
|
|
packet_receive(&p);
|
|
}
|
|
|
|
void* network_duplicate_address(u8 localIndex) {
|
|
assert(localIndex < MAX_PLAYERS);
|
|
return gNetworkSystem->dup_addr(localIndex);
|
|
}
|
|
|
|
void network_reset_reconnect_and_rehost(void) {
|
|
gNetworkStartupTimer = 0;
|
|
sNetworkReconnectTimer = 0;
|
|
sNetworkRehostTimer = 0;
|
|
sNetworkReconnectType = NS_SOCKET;
|
|
gDiscordReconnecting = false;
|
|
}
|
|
|
|
void network_reconnect_begin(void) {
|
|
if (sNetworkReconnectTimer > 0) {
|
|
return;
|
|
}
|
|
|
|
sNetworkReconnectTimer = 2 * 30;
|
|
sNetworkReconnectType = (gNetworkSystem == &gNetworkSystemDiscord)
|
|
? NS_DISCORD
|
|
: NS_SOCKET;
|
|
|
|
gDiscordReconnecting = true;
|
|
network_shutdown(false, false, false);
|
|
gDiscordReconnecting = false;
|
|
|
|
djui_connect_menu_open();
|
|
}
|
|
|
|
static void network_reconnect_update(void) {
|
|
if (sNetworkReconnectTimer <= 0) { return; }
|
|
if (--sNetworkReconnectTimer != 0) { return; }
|
|
|
|
if (sNetworkReconnectType == NS_SOCKET) {
|
|
network_set_system(NS_SOCKET);
|
|
}
|
|
|
|
gDiscordReconnecting = true;
|
|
network_init(NT_CLIENT);
|
|
gDiscordReconnecting = false;
|
|
|
|
network_send_mod_list_request();
|
|
}
|
|
|
|
bool network_is_reconnecting(void) {
|
|
return sNetworkReconnectTimer > 0;
|
|
}
|
|
|
|
void network_rehost_begin(void) {
|
|
for (int i = 1; i < MAX_PLAYERS; i++) {
|
|
struct NetworkPlayer* np = &gNetworkPlayers[i];
|
|
if (!np->connected) { continue; }
|
|
|
|
network_send_kick(i, EKT_REJOIN);
|
|
network_player_disconnected(i);
|
|
}
|
|
|
|
gDiscordReconnecting = true;
|
|
network_shutdown(false, false, false);
|
|
gDiscordReconnecting = false;
|
|
|
|
sNetworkRehostTimer = 2;
|
|
}
|
|
|
|
static void network_rehost_update(void) {
|
|
extern void djui_panel_do_host(void);
|
|
if (sNetworkRehostTimer <= 0) { return; }
|
|
if (--sNetworkRehostTimer != 0) { return; }
|
|
|
|
gDiscordReconnecting = true;
|
|
djui_panel_do_host();
|
|
#ifdef DISCORD_SDK
|
|
if (sNetworkReconnectType == NS_DISCORD) {
|
|
discord_activity_update(true);
|
|
}
|
|
#endif
|
|
gDiscordReconnecting = false;
|
|
}
|
|
|
|
static void network_update_area_timer(void) {
|
|
bool brokenClock = false;
|
|
#ifdef DEVELOPMENT
|
|
static u16 skipClockCount = 0;
|
|
static u16 updateClockCount = 1;
|
|
if (updateClockCount > 0) {
|
|
updateClockCount--;
|
|
if (updateClockCount <= 0 || updateClockCount > 120) {
|
|
skipClockCount = rand() % 30;
|
|
}
|
|
}
|
|
else {
|
|
skipClockCount--;
|
|
if (skipClockCount <= 0 || skipClockCount > 60) {
|
|
updateClockCount = rand() % 120;
|
|
}
|
|
}
|
|
//brokenClock = (skipClockCount > 0);
|
|
#endif
|
|
if (!brokenClock) {
|
|
// update network area timer
|
|
u32 desiredNAT = gNetworkAreaTimer + 1;
|
|
gNetworkAreaTimer = (clock_elapsed_ticks() - gNetworkAreaTimerClock);
|
|
if (gNetworkAreaTimer < desiredNAT) {
|
|
gNetworkAreaTimer++;
|
|
}
|
|
else if (gNetworkAreaTimer > desiredNAT) {
|
|
gNetworkAreaTimer--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void network_update(void) {
|
|
|
|
if (gNetworkStartupTimer > 0) {
|
|
gNetworkStartupTimer--;
|
|
}
|
|
|
|
network_rehost_update();
|
|
network_reconnect_update();
|
|
|
|
// check for level loaded event
|
|
if (networkLoadingLevel < LOADING_LEVEL_THRESHOLD) {
|
|
networkLoadingLevel++;
|
|
if (!gNetworkAreaLoaded && networkLoadingLevel >= LOADING_LEVEL_THRESHOLD) {
|
|
gNetworkAreaLoaded = true;
|
|
network_on_loaded_area();
|
|
}
|
|
}
|
|
|
|
// update network area timer
|
|
network_update_area_timer();
|
|
|
|
// send out update packets
|
|
if (gNetworkType != NT_NONE) {
|
|
network_player_update();
|
|
if (sCurrPlayMode == PLAY_MODE_NORMAL || sCurrPlayMode == PLAY_MODE_PAUSED) {
|
|
network_update_player();
|
|
network_update_objects();
|
|
}
|
|
}
|
|
|
|
// receive packets
|
|
if (gNetworkSystem != NULL) {
|
|
gNetworkSystem->update();
|
|
}
|
|
|
|
// update reliable and ordered packets
|
|
if (gNetworkType != NT_NONE) {
|
|
network_update_reliable();
|
|
packet_ordered_update();
|
|
}
|
|
|
|
sync_objects_update();
|
|
|
|
// update level/area request timers
|
|
/*struct NetworkPlayer* np = gNetworkPlayerLocal;
|
|
if (np != NULL && !np->currLevelSyncValid) {
|
|
gNetworkRequestLocationTimer++;
|
|
if (gNetworkRequestLocationTimer > 30 * 10) {
|
|
// find a NetworkPlayer around that location
|
|
struct NetworkPlayer *npLevelAreaMatch = get_network_player_from_area(np->currCourseNum, np->currActNum, np->currLevelNum, np->currAreaIndex);
|
|
struct NetworkPlayer *npLevelMatch = get_network_player_from_level(np->currCourseNum, np->currActNum, np->currLevelNum);
|
|
struct NetworkPlayer *npAny = (npLevelAreaMatch == NULL) ? npLevelMatch : npLevelAreaMatch;
|
|
|
|
bool inCredits = (np->currActNum == 99);
|
|
if (gNetworkType == NT_SERVER && (npAny == NULL || inCredits)) {
|
|
// no NetworkPlayer in the level
|
|
network_send_sync_valid(np, np->currCourseNum, np->currActNum, np->currLevelNum, np->currAreaIndex);
|
|
return;
|
|
}
|
|
|
|
// matching NetworkPlayer is client
|
|
if (npAny == npLevelAreaMatch) {
|
|
network_send_level_area_request(np, npAny);
|
|
} else {
|
|
network_send_level_request(np, npAny);
|
|
}
|
|
}
|
|
}*/
|
|
|
|
}
|
|
|
|
void network_register_mod(char* modName) {
|
|
if (string_linked_list_contains(&gRegisteredMods, modName)) { return; }
|
|
string_linked_list_append(&gRegisteredMods, modName);
|
|
}
|
|
|
|
void network_shutdown(bool sendLeaving, bool exiting, bool popup) {
|
|
if (gDjuiChatBox != NULL) {
|
|
djui_base_destroy(&gDjuiChatBox->base);
|
|
gDjuiChatBox = NULL;
|
|
}
|
|
|
|
gNetworkSentJoin = false;
|
|
|
|
network_forget_all_reliable();
|
|
if (gNetworkType == NT_NONE) { return; }
|
|
if (gNetworkSystem == NULL) { LOG_ERROR("no network system attached"); return; }
|
|
|
|
if (gNetworkPlayerLocal != NULL && sendLeaving) { network_send_leaving(gNetworkPlayerLocal->globalIndex); }
|
|
network_player_shutdown(popup);
|
|
gNetworkSystem->shutdown();
|
|
|
|
if (gNetworkServerAddr != NULL) {
|
|
free(gNetworkServerAddr);
|
|
gNetworkServerAddr = NULL;
|
|
}
|
|
gNetworkPlayerServer = NULL;
|
|
|
|
if (sNetworkReconnectTimer <= 0 || sNetworkReconnectType != NS_DISCORD) {
|
|
gNetworkType = NT_NONE;
|
|
}
|
|
|
|
|
|
#ifdef DISCORD_SDK
|
|
network_set_system(NS_DISCORD);
|
|
#endif
|
|
|
|
if (exiting) { return; }
|
|
|
|
// reset other stuff
|
|
extern u8* gOverrideEeprom;
|
|
gOverrideEeprom = NULL;
|
|
extern u8 gOverrideFreezeCamera;
|
|
gOverrideFreezeCamera = false;
|
|
gDjuiHudLockMouse = false;
|
|
gOverrideNear = 0;
|
|
gOverrideFar = 0;
|
|
gOverrideFOV = 0;
|
|
gLightingDir[0] = 0;
|
|
gLightingDir[1] = 0;
|
|
gLightingDir[2] = 0;
|
|
gOverrideBackground = -1;
|
|
gOverrideEnvFx = -1;
|
|
gDjuiRenderBehindHud = false;
|
|
dynos_mod_shutdown();
|
|
mods_clear(&gActiveMods);
|
|
mods_clear(&gRemoteMods);
|
|
smlua_shutdown();
|
|
extern s16 gChangeLevel;
|
|
gChangeLevel = LEVEL_CASTLE_GROUNDS;
|
|
if (gSkipInterpolationTitleScreen || find_object_with_behavior(bhvActSelector) != NULL) {
|
|
dynos_warp_to_level(LEVEL_CASTLE_GROUNDS, 1, 0);
|
|
}
|
|
network_player_init();
|
|
camera_set_use_course_specific_settings(true);
|
|
free_vtx_scroll_targets();
|
|
gMarioStates[0].cap = 0;
|
|
extern s16 gTTCSpeedSetting;
|
|
gTTCSpeedSetting = 0;
|
|
|
|
struct Controller* cnt = gMarioStates[0].controller;
|
|
cnt->rawStickX = 0;
|
|
cnt->rawStickY = 0;
|
|
cnt->stickX = 0;
|
|
cnt->stickY = 0;
|
|
cnt->stickMag = 0;
|
|
cnt->buttonDown = 0;
|
|
cnt->buttonPressed = 0;
|
|
cnt->extStickX = 0;
|
|
cnt->extStickY = 0;
|
|
|
|
extern void save_file_load_all(UNUSED u8 reload);
|
|
save_file_load_all(TRUE);
|
|
extern void save_file_set_using_backup_slot(bool usingBackupSlot);
|
|
save_file_set_using_backup_slot(false);
|
|
|
|
extern s16 gMenuMode;
|
|
gMenuMode = -1;
|
|
|
|
extern bool gIsModerator;
|
|
gIsModerator = false;
|
|
|
|
djui_panel_shutdown();
|
|
extern bool gDjuiInMainMenu;
|
|
if (!gDjuiInMainMenu) {
|
|
gDjuiInMainMenu = true;
|
|
djui_panel_main_create(NULL);
|
|
}
|
|
}
|