Moderator (#80)

This commit is contained in:
Isaac 2022-05-10 16:11:39 +10:00 committed by GitHub
parent c7cfa1be54
commit 701fddf4dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 562 additions and 177 deletions

View file

@ -728,7 +728,7 @@ else ifeq ($(WINDOWS_BUILD),1)
LD := $(CXX)
endif
else
LD := $(CROSS)ld
LD := $(CXX)
endif
AR := $(CROSS)ar
@ -906,17 +906,22 @@ else
LDFLAGS += -Llib/lua/linux -l:liblua53.a
endif
# Network
# Network/Discord/Bass (ugh, needs cleanup)
ifeq ($(WINDOWS_BUILD),1)
LDFLAGS += -L"ws2_32" -lwsock32
ifeq ($(DISCORD_SDK),1)
LDFLAGS += -Wl,-Bdynamic -L./lib/discordsdk/ -L./lib/bass/ -ldiscord_game_sdk -lbass -lbass_fx -Wl,-Bstatic
else
LDFLAGS += -Wl,-Bdynamic -L./lib/bass/ -lbass -lbass_fx -Wl,-Bstatic
endif
else
ifeq ($(DISCORD_SDK),1)
LDFLAGS += -ldiscord_game_sdk -lbass -lbass_fx -Wl,-rpath . -Wl,-rpath lib/discordsdk -Wl,-rpath lib/bass
else
LDFLAGS += -lbass -lbass_fx -Wl,-rpath . -Wl,-rpath lib/bass
endif
endif
# Prevent a crash with -sopt
export LANG := C

View file

@ -53,5 +53,8 @@ const char* dynos_level_get_token(u32 index);
Trajectory* dynos_level_get_trajectory(const char* name);
void dynos_level_load_background(void *ptr);
// -- other -- //
void dynos_mod_shutdown(void);
#endif
#endif

View file

@ -12,6 +12,8 @@ extern "C" {
#define POINTER_CODE (u32) 0x52544E50
#define LUA_VAR_CODE (u32) 0x5641554C
#define MOD_PACK_INDEX 99
//
// Enums
//
@ -644,6 +646,8 @@ void *DynOS_SwapCmd(void *aCmd);
void *DynOS_UpdateCmd(void *aCmd);
void DynOS_UpdateGfx();
bool DynOS_IsTransitionActive();
void DynOS_Mod_Update();
void DynOS_Mod_Shutdown();
void DynOS_ReturnToMainMenu();
//
@ -695,6 +699,7 @@ s32 DynOS_Level_GetCount();
const s32 *DynOS_Level_GetList();
s32 DynOS_Level_GetCourse(s32 aLevel);
void DynOS_Level_Override(void* originalScript, void* newScript);
void DynOS_Level_Unoverride();
const void *DynOS_Level_GetScript(s32 aLevel);
const u8 *DynOS_Level_GetName(s32 aLevel, bool aDecaps, bool aAddCourseNumber);
const u8 *DynOS_Level_GetActName(s32 aLevel, s32 aAct, bool aDecaps, bool aAddStarNumber);
@ -764,6 +769,7 @@ void DynOS_Actor_Valid(const void* aGeoref, ActorGfx& aActorGfx);
void DynOS_Actor_Invalid(const void* aGeoref, s32 aPackIndex);
void DynOS_Actor_Override(void** aSharedChild);
void DynOS_Actor_Override_All(void);
void DynOS_Actor_ModShutdown();
//
// Anim Manager
@ -784,6 +790,7 @@ void DynOS_Tex_Activate(DataNode<TexData>* aNode, bool aCustomTexture);
void DynOS_Tex_Deactivate(DataNode<TexData>* aNode);
void DynOS_Tex_AddCustom(const SysPath &aFilename, const char *aTexName);
bool DynOS_Tex_Get(const char* aTexName, struct TextureInfo* aOutTexInfo);
void DynOS_Tex_ModShutdown();
//
// Lvl Manager
@ -797,6 +804,7 @@ DataNode<MovtexQC>* DynOS_Lvl_GetMovtexQuadCollection(s32 index);
Trajectory* DynOS_Lvl_GetTrajectory(const char* aName);
void DynOS_Lvl_LoadBackground(void *aPtr);
void* DynOS_Lvl_Override(void *aCmd);
void DynOS_Lvl_ModShutdown();
//
// Col Manager
@ -804,6 +812,7 @@ void* DynOS_Lvl_Override(void *aCmd);
void DynOS_Col_Activate(const SysPath &aFilePath, const char *aCollisionName);
Collision* DynOS_Col_Get(const char* collisionName);
void DynOS_Col_ModShutdown();
//
// Movtexqc Manager
@ -812,6 +821,7 @@ Collision* DynOS_Col_Get(const char* collisionName);
void DynOS_MovtexQC_Register(const char* name, s16 level, s16 area, s16 type);
DataNode<MovtexQC>* DynOS_MovtexQC_GetFromId(u32 id);
DataNode<MovtexQC>* DynOS_MovtexQC_GetFromIndex(s32 index);
void DynOS_MovtexQC_ModShutdown();
//
// Bin

View file

@ -156,4 +156,9 @@ void dynos_level_load_background(void *ptr) {
DynOS_Lvl_LoadBackground(ptr);
}
// -- other -- //
void dynos_mod_shutdown(void) {
DynOS_Mod_Shutdown();
}
}

View file

@ -2,6 +2,9 @@
extern "C" {
}
bool gDynosModShutdown = false;
void DynOS_Gfx_Update() {
DynOS_Mod_Update();
DynOS_Tex_Update();
}

View file

@ -46,6 +46,7 @@ struct DynosWarp {
};
static void *sDynosLevelScripts[LEVEL_COUNT] = { NULL };
static void *sDynosLevelScriptsOriginal[LEVEL_COUNT] = { NULL };
static Array<DynosWarp> sDynosLevelWarps[LEVEL_COUNT] = { Array<DynosWarp>() };
static Array<s32> sDynosLevelList = Array<s32>(); // Ordered by Course Id, COURSE_NONE excluded
@ -90,6 +91,7 @@ static s32 DynOS_Level_PreprocessMasterScript(u8 aType, void *aCmd) {
void *_Script = (void *) DynOS_Level_CmdGet(aCmd, 0x0C);
if (sDynosLevelNum >= 0 && sDynosLevelNum < LEVEL_COUNT && !sDynosLevelScripts[sDynosLevelNum]) {
sDynosLevelScripts[sDynosLevelNum] = _Script;
sDynosLevelScriptsOriginal[sDynosLevelNum] = _Script;
}
sDynosLevelNum = -1;
return 2;
@ -237,6 +239,15 @@ void DynOS_Level_Override(void* originalScript, void* newScript) {
}
}
void DynOS_Level_Unoverride() {
for (s32 i = 0; i < LEVEL_COUNT; i++) {
sDynosCurrentLevelNum = i;
sDynosLevelWarps[i].Clear();
sDynosLevelScripts[i] = sDynosLevelScriptsOriginal[i];
DynOS_Level_ParseScript(sDynosLevelScripts[i], DynOS_Level_PreprocessScript);
}
}
const void *DynOS_Level_GetScript(s32 aLevel) {
DynOS_Level_Init();
return sDynosLevelScripts[aLevel];

View file

@ -66,3 +66,23 @@ void DynOS_UpdateGfx() {
bool DynOS_IsTransitionActive() {
return gWarpTransition.isActive;
}
//
// Misc
//
static bool sDynosModShutdown = false;
void DynOS_Mod_Update() {
if (sDynosModShutdown) {
sDynosModShutdown = false;
DynOS_Actor_ModShutdown();
DynOS_Col_ModShutdown();
DynOS_Lvl_ModShutdown();
DynOS_MovtexQC_ModShutdown();
DynOS_Tex_ModShutdown();
}
}
void DynOS_Mod_Shutdown() {
sDynosModShutdown = true;
}

View file

@ -44,18 +44,19 @@ void DynOS_Actor_AddCustom(const SysPath &aFilename, const char *aActorName) {
return;
}
// Alloc and init the actors gfx list
ActorGfx actorGfx = { 0 };
actorGfx.mGfxData = _GfxData;
actorGfx.mGraphNode = (GraphNode *) DynOS_Geo_GetGraphNode(geoLayout, false);
actorGfx.mPackIndex = MOD_PACK_INDEX;
actorGfx.mGraphNode->georef = georef;
// Add to custom actors
if (georef == NULL) {
DynosCustomActors().Add({ actorName, geoLayout });
georef = geoLayout;
}
// Alloc and init the actors gfx list
ActorGfx actorGfx;
actorGfx.mGfxData = _GfxData;
actorGfx.mGraphNode = (GraphNode *) DynOS_Geo_GetGraphNode(geoLayout, false);
actorGfx.mPackIndex = 99;
// Add to list
DynOS_Actor_Valid(georef, actorGfx);
}
@ -150,3 +151,27 @@ void DynOS_Actor_Override_All(void) {
}
}
}
void DynOS_Actor_ModShutdown() {
auto& _DynosCustomActors = DynosCustomActors();
while (_DynosCustomActors.Count() > 0) {
auto& pair = _DynosCustomActors[0];
DynOS_Actor_Invalid(pair.second, MOD_PACK_INDEX);
free((void*)pair.first);
_DynosCustomActors.Remove(0);
}
auto& _ValidActors = DynosValidActors();
for (auto it = _ValidActors.cbegin(); it != _ValidActors.cend();) {
auto& actorGfx = it->second;
if (actorGfx.mPackIndex == MOD_PACK_INDEX) {
free(actorGfx.mGraphNode);
DynOS_Gfx_Free(actorGfx.mGfxData);
_ValidActors.erase(it++);
} else {
++it;
}
}
DynOS_Actor_Override_All();
}

View file

@ -54,3 +54,13 @@ Collision* DynOS_Col_Get(const char* collisionName) {
// check builtin collisions
return (Collision*)DynOS_Builtin_LvlCol_GetFromName(collisionName);
}
void DynOS_Col_ModShutdown() {
auto& _DynosCollisions = DynosCollisions();
while (_DynosCollisions.Count() > 0) {
auto& pair = _DynosCollisions[0];
free((void*)pair.first);
Delete(pair.second);
_DynosCollisions.Remove(0);
}
}

View file

@ -20,6 +20,22 @@ Array<Pair<const char*, GfxData*>> &DynOS_Lvl_GetArray() {
return sDynosCustomLevelScripts;
}
void DynOS_Lvl_ModShutdown() {
DynOS_Level_Unoverride();
auto& _CustomLevelScripts = DynOS_Lvl_GetArray();
while (_CustomLevelScripts.Count() > 0) {
auto& pair = _CustomLevelScripts[0];
DynOS_Tex_Invalid(pair.second);
Delete(pair.second);
free((void*)pair.first);
_CustomLevelScripts.Remove(0);
}
auto& _OverrideLevelScripts = DynosOverrideLevelScripts();
_OverrideLevelScripts.Clear();
}
void DynOS_Lvl_Activate(s32 modIndex, const SysPath &aFilename, const char *aLevelName) {
auto& _CustomLevelScripts = DynOS_Lvl_GetArray();
auto& _OverrideLevelScripts = DynosOverrideLevelScripts();

View file

@ -67,3 +67,12 @@ DataNode<MovtexQC>* DynOS_MovtexQC_GetFromIndex(s32 index) {
return mMovtexQCs[index];
}
void DynOS_MovtexQC_ModShutdown() {
auto& _DynosRegisteredMovtexQCs = DynosRegisteredMovtexQCs();
while (_DynosRegisteredMovtexQCs.Count() > 0) {
auto& registered = _DynosRegisteredMovtexQCs[0];
Delete(registered.dataNode);
_DynosRegisteredMovtexQCs.Remove(0);
}
}

View file

@ -422,6 +422,7 @@ void DynOS_Tex_AddCustom(const SysPath &aFilename, const char *aTexName) {
// Load
SysPath _PackFolder = "";
DataNode<TexData>* _Node = DynOS_Tex_LoadFromBinary(_PackFolder, aFilename, _TexName, false);
free(_TexName);
if (_Node) {
DynOS_Tex_Activate(_Node, true);
}
@ -468,3 +469,11 @@ bool DynOS_Tex_Get(const char* aTexName, struct TextureInfo* aOutTexInfo) {
aOutTexInfo->texture = (u8*)info->pointer;
return true;
}
void DynOS_Tex_ModShutdown() {
auto& _DynosCustomTexs = DynosCustomTexs();
while (_DynosCustomTexs.Count() > 0) {
auto& pair = _DynosCustomTexs[0];
DynOS_Tex_Deactivate(pair.second);
}
}

View file

@ -95,26 +95,10 @@ function spawn_custom_level_objects()
return
end
-- look for existing powerups
local obj = obj_get_first(OBJ_LIST_LEVEL)
while obj ~= nil do
local behaviorId = get_id_from_behavior(obj.behavior)
if behaviorId == id_bhvItemBox then
-- find closest position to put it in
local objPos = { x = obj.oPosX, y = obj.oPosY, z = obj.oPosZ }
for i in pairs(gLevelData.powerups) do
local powPos = gLevelData.powerups[i].pos
local tempPos = { x = powPos.x, y = objPos.y, z = powPos.z }
local dist = vec3f_dist(objPos, tempPos)
if dist < 5 then
gLevelData.powerups[i].obj = obj
end
end
end
-- iterate
obj = obj_get_next(obj)
-- only handle powerups if we're sync valid
np = gNetworkPlayers[0]
if (not np.currAreaSyncValid) or (not np.currLevelSyncValid) then
return
end
-- spawn missing powerups

View file

@ -1,6 +1,6 @@
-- name: Shell Rush
-- description: Race around SM64 levels on shells.\n\nCollect powerups such as red shells, green shells, bananas, and mushrooms.\n\nOnly use a save that has lowered the water in the moat.
-- incompatible: gamemode
-- incompatible: gamemode moveset
DEBUG = false
UNST22 = true -- gotta work around unst 22 bugs :(

View file

@ -261,6 +261,20 @@ function select_powerup()
local s = gPlayerSyncTable[0]
local pick = math.random(1, POWERUP_MAX-1)
local luck = math.random() < 0.33
-- don't give mushrooms when first place
local rank = 0
for i in pairs(gRankings) do
if gRankings[i].playerIndex == 0 then
rank = i
end
end
if rank <= 1 then
if pick == POWERUP_MUSHROOM then
pick = POWERUP_GREEN_SHELL
end
end
if luck then
s.powerup[0] = pick
s.powerup[1] = pick

View file

@ -123,7 +123,7 @@ function race_update()
end
local np = gNetworkPlayers[0]
if gGlobalSyncTable.gotoLevel ~= -1 then
if gGlobalSyncTable.gotoLevel ~= -1 and np.currAreaSyncValid and np.currLevelSyncValid then
if np.currLevelNum ~= gGlobalSyncTable.gotoLevel then
if gGlobalSyncTable.gotoLevel == LEVEL_CASTLE_GROUNDS then
warp_to_castle(LEVEL_VCUTM)

View file

@ -208,11 +208,13 @@ void bookshelf_manager_act_2(void) {
if (o->oTimer > 100) {
if (o->oSyncID != 0 && gSyncObjects[o->oSyncID].owned) {
o->parentObj = cur_obj_nearest_object_with_behavior(bhvHauntedBookshelf);
o->parentObj->oAction = 1;
o->oPosX = o->parentObj->oPosX;
o->oAction = 3;
network_send_object(o);
network_send_object(o->parentObj);
if (o->parentObj != NULL) {
o->parentObj->oAction = 1;
o->oPosX = o->parentObj->oPosX;
network_send_object(o->parentObj);
}
}
} else if (o->oTimer == 30) {
play_puzzle_jingle();

View file

@ -28,10 +28,12 @@ void bhv_piranha_plant_waking_bubbles_loop(void) {
*/
void bhv_piranha_plant_bubble_loop(void) {
struct Object *parent = o->parentObj; // the Piranha Plant
if (parent == NULL) { return; }
f32 scale = 0;
s32 i;
s32 frame = parent->header.gfx.animInfo.animFrame;
// TODO: rename lastFrame if it is inaccurate
if (parent->header.gfx.animInfo.curAnim == NULL) { return; }
s32 lastFrame = parent->header.gfx.animInfo.curAnim->loopEnd - 2;
s32 UNUSED unused;
f32 doneShrinkingFrame; // the first frame after shrinking is done

View file

@ -30,11 +30,7 @@
// FIXME: I'm not sure all of these variables belong in this file, but I don't
// know of a good way to split them
#ifdef UNSTABLE_BRANCH
struct Controller gControllers[MAX_PLAYERS];
#else
struct Controller gControllers[3];
#endif
struct SPTask *gGfxSPTask;
Gfx *gDisplayListHead;
u8 *gGfxPoolEnd;

View file

@ -22,11 +22,7 @@ struct DemoInput
u8 buttonMask;
};
#ifdef UNSTABLE_BRANCH
extern struct Controller gControllers[MAX_PLAYERS];
#else
extern struct Controller gControllers[3];
#endif
extern OSContStatus gControllerStatuses[4];
extern OSContPad gControllerPads[4];
extern OSMesgQueue gGameVblankQueue;

View file

@ -2159,28 +2159,6 @@ void init_single_mario(struct MarioState* m) {
m->marioObj->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
}
// offset spawning
u8 connectedPlayers = network_player_connected_count();
u8 globalIndex = gNetworkPlayers[m->playerIndex].globalIndex;
u16 spawnAngle = m->faceAngle[1] + 0x4000 + 0x10000 * ((f32)globalIndex / ((f32)connectedPlayers + 1));
f32 spawnMag = 60.0f * ((connectedPlayers + 1) / 2.0f);
if (spawnMag > 120) { spawnMag = 120; }
// figure out if we should apply offset
u8 nearbyPlayers = 1;
for (s32 i = 1; i < MAX_PLAYERS; i++) {
struct NetworkPlayer *np = &gNetworkPlayers[i];
if (!np->connected) { continue; }
if (np->currCourseNum == gCurrCourseNum && np->currLevelNum == gCurrLevelNum && np->currActNum == gCurrActStarNum && np->currAreaIndex == gCurrAreaIndex) {
nearbyPlayers++;
}
}
if (nearbyPlayers > 1) {
m->pos[0] -= spawnMag * sins(spawnAngle);
m->pos[2] -= spawnMag * coss(spawnAngle);
}
m->floorHeight = find_floor(m->pos[0], m->pos[1], m->pos[2], &m->floor);
if (m->pos[1] < m->floorHeight) {

View file

@ -652,6 +652,7 @@ Gfx* geo_switch_mario_cap_on_off(s32 callContext, struct GraphNode* node, UNUSED
struct MarioBodyState* bodyState = geo_get_body_state();
if (callContext == GEO_CONTEXT_RENDER) {
if (switchCase == NULL || bodyState == NULL) { return NULL; }
switchCase->selectedCase = bodyState->capState & 1;
while (next != node) {
if (next->type == GRAPH_NODE_TYPE_TRANSLATION_ROTATION) {
@ -773,9 +774,9 @@ Gfx* geo_render_mirror_mario(s32 callContext, struct GraphNode* node, UNUSED Mat
gMirrorMario[i].pos[0] = mirroredX + MIRROR_X;
gMirrorMario[i].angle[1] = -gMirrorMario[i].angle[1];
gMirrorMario[i].scale[0] *= -1.0f;
((struct GraphNode *) &gMirrorMario)->flags |= 1;
gMirrorMario[i].node.flags |= GRAPH_RENDER_ACTIVE;
} else {
((struct GraphNode *) &gMirrorMario)->flags &= ~1;
gMirrorMario[i].node.flags &= ~GRAPH_RENDER_ACTIVE;
}
break;
}

View file

@ -245,6 +245,7 @@ Gfx *geo_choose_area_ext(UNUSED s32 callContext, struct GraphNode *node, UNUSED
}
void obj_update_pos_from_parent_transformation(Mat4 a0, struct Object *a1) {
if (a1 == NULL) { return; }
f32 spC = a1->oParentRelativePosX;
f32 sp8 = a1->oParentRelativePosY;
f32 sp4 = a1->oParentRelativePosZ;
@ -255,6 +256,7 @@ void obj_update_pos_from_parent_transformation(Mat4 a0, struct Object *a1) {
}
void obj_apply_scale_to_matrix(struct Object *obj, Mat4 dst, Mat4 src) {
if (obj == NULL) { return; }
dst[0][0] = src[0][0] * obj->header.gfx.scale[0];
dst[1][0] = src[1][0] * obj->header.gfx.scale[1];
dst[2][0] = src[2][0] * obj->header.gfx.scale[2];
@ -306,6 +308,7 @@ void create_transformation_from_matrices(Mat4 a0, Mat4 a1, Mat4 a2) {
}
void obj_set_held_state(struct Object *obj, const BehaviorScript *heldBehavior) {
if (obj == NULL) { return; }
obj->parentObj = o;
if (obj->oFlags & OBJ_FLAG_HOLDABLE) {
@ -363,6 +366,7 @@ void cur_obj_forward_vel_approach_upward(f32 target, f32 increment) {
}
s32 approach_f32_signed(f32 *value, f32 target, f32 increment) {
if (value == NULL) { return 0; }
s32 reachedTarget = FALSE;
*value += increment;
@ -436,6 +440,8 @@ s32 cur_obj_rotate_yaw_toward(s16 target, s16 increment) {
}
s16 obj_angle_to_object(struct Object *obj1, struct Object *obj2) {
if (obj1 == NULL || obj2 == NULL) { return 0; }
f32 z1, x1, z2, x2;
s16 angle;
@ -447,6 +453,7 @@ s16 obj_angle_to_object(struct Object *obj1, struct Object *obj2) {
}
s16 obj_pitch_to_object(struct Object* obj, struct Object* target) {
if (obj == NULL) { return 0; }
f32 a, b, c, d;
a = target->oPosX - obj->oPosX;
c = target->oPosZ - obj->oPosZ;
@ -459,6 +466,7 @@ s16 obj_pitch_to_object(struct Object* obj, struct Object* target) {
}
s16 obj_angle_to_point(struct Object *obj, f32 pointX, f32 pointZ) {
if (obj == NULL) { return 0; }
f32 z1, x1, z2, x2;
s16 angle;
@ -470,6 +478,7 @@ s16 obj_angle_to_point(struct Object *obj, f32 pointX, f32 pointZ) {
}
s16 obj_turn_toward_object(struct Object *obj, struct Object *target, s16 angleIndex, s16 turnAmount) {
if (obj == NULL || target == NULL) { return 0; }
f32 a, b, c, d;
UNUSED s32 unused;
s16 targetAngle = 0;
@ -505,18 +514,21 @@ s16 obj_turn_toward_object(struct Object *obj, struct Object *target, s16 angleI
}
void obj_set_parent_relative_pos(struct Object *obj, s16 relX, s16 relY, s16 relZ) {
if (obj == NULL) { return; }
obj->oParentRelativePosX = relX;
obj->oParentRelativePosY = relY;
obj->oParentRelativePosZ = relZ;
}
void obj_set_pos(struct Object *obj, s16 x, s16 y, s16 z) {
if (obj == NULL) { return; }
obj->oPosX = x;
obj->oPosY = y;
obj->oPosZ = z;
}
void obj_set_angle(struct Object *obj, s16 pitch, s16 yaw, s16 roll) {
if (obj == NULL) { return; }
obj->oFaceAnglePitch = pitch;
obj->oFaceAngleYaw = yaw;
obj->oFaceAngleRoll = roll;
@ -762,6 +774,7 @@ void linear_mtxf_transpose_mul_vec3f(Mat4 m, Vec3f dst, Vec3f v) {
}
void obj_apply_scale_to_transform(struct Object *obj) {
if (obj == NULL) { return; }
f32 scaleX = obj->header.gfx.scale[0];
f32 scaleY = obj->header.gfx.scale[1];
f32 scaleZ = obj->header.gfx.scale[2];
@ -780,18 +793,21 @@ void obj_apply_scale_to_transform(struct Object *obj) {
}
void obj_copy_scale(struct Object *dst, struct Object *src) {
if (dst == NULL || src == NULL) { return; }
dst->header.gfx.scale[0] = src->header.gfx.scale[0];
dst->header.gfx.scale[1] = src->header.gfx.scale[1];
dst->header.gfx.scale[2] = src->header.gfx.scale[2];
}
void obj_scale_xyz(struct Object *obj, f32 xScale, f32 yScale, f32 zScale) {
if (obj == NULL) { return; }
obj->header.gfx.scale[0] = xScale;
obj->header.gfx.scale[1] = yScale;
obj->header.gfx.scale[2] = zScale;
}
void obj_scale(struct Object *obj, f32 scale) {
if (obj == NULL) { return; }
obj->header.gfx.scale[0] = scale;
obj->header.gfx.scale[1] = scale;
obj->header.gfx.scale[2] = scale;
@ -828,6 +844,7 @@ void cur_obj_init_animation_with_accel_and_sound(s32 animIndex, f32 accel) {
}
void obj_init_animation_with_sound(struct Object *obj, const struct Animation * const* animations, s32 animIndex) {
if (obj == NULL) { return; }
struct Animation **anims = (struct Animation **)animations;
obj->oAnimations = (struct Animation **)animations;
if (anims != NULL) {
@ -837,6 +854,7 @@ void obj_init_animation_with_sound(struct Object *obj, const struct Animation *
}
void cur_obj_enable_rendering_and_become_tangible(struct Object *obj) {
if (obj == NULL) { return; }
obj->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
obj->oIntangibleTimer = 0;
}
@ -846,6 +864,7 @@ void cur_obj_enable_rendering(void) {
}
void cur_obj_disable_rendering_and_become_intangible(struct Object *obj) {
if (obj == NULL) { return; }
obj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
obj->oIntangibleTimer = -1;
}
@ -863,6 +882,7 @@ void cur_obj_hide(void) {
}
void cur_obj_set_pos_relative(struct Object *other, f32 dleft, f32 dy, f32 dforward) {
if (other == NULL) { return; }
f32 facingZ = coss(other->oMoveAngleYaw);
f32 facingX = sins(other->oMoveAngleYaw);
@ -895,6 +915,7 @@ void cur_obj_unused_init_on_floor(void) {
}
void obj_set_face_angle_to_move_angle(struct Object *obj) {
if (obj == NULL) { return; }
obj->oFaceAnglePitch = obj->oMoveAnglePitch;
obj->oFaceAngleYaw = obj->oMoveAngleYaw;
obj->oFaceAngleRoll = obj->oMoveAngleRoll;
@ -1614,6 +1635,7 @@ f32 increment_velocity_toward_range(f32 value, f32 center, f32 zeroThreshold, f3
}
s32 obj_check_if_collided_with_object(struct Object *obj1, struct Object *obj2) {
if (obj1 == NULL) { return FALSE; }
s32 i;
for (i = 0; i < obj1->numCollidedObjs; i++) {
if (obj1->collidedObjs[i] == obj2) {
@ -1751,10 +1773,12 @@ void set_mario_interact_hoot_if_in_range(UNUSED s32 sp0, UNUSED s32 sp4, f32 sp8
}
void obj_set_billboard(struct Object *obj) {
if (obj == NULL) { return; }
obj->header.gfx.node.flags |= GRAPH_RENDER_BILLBOARD;
}
void obj_set_cylboard(struct Object *obj) {
if (obj == NULL) { return; }
obj->header.gfx.node.flags |= GRAPH_RENDER_CYLBOARD;
}
@ -1771,6 +1795,7 @@ void cur_obj_set_hurtbox_radius_and_height(f32 radius, f32 height) {
void obj_spawn_loot_coins(struct Object *obj, s32 numCoins, f32 sp30,
const BehaviorScript *coinBehavior,
s16 posJitter, s16 model) {
if (obj == NULL) { return; }
s32 i;
f32 spawnHeight;
struct Surface *floor;
@ -2076,6 +2101,7 @@ void obj_set_gfx_pos_at_obj_pos(struct Object *obj1, struct Object *obj2) {
* coordinates, and then add it to the vector at posIndex.
*/
void obj_translate_local(struct Object *obj, s16 posIndex, s16 localTranslateIndex) {
if (obj == NULL) { return; }
f32 dx = obj->rawData.asF32[localTranslateIndex + 0];
f32 dy = obj->rawData.asF32[localTranslateIndex + 1];
f32 dz = obj->rawData.asF32[localTranslateIndex + 2];
@ -2089,6 +2115,7 @@ void obj_translate_local(struct Object *obj, s16 posIndex, s16 localTranslateInd
}
void obj_build_transform_from_pos_and_angle(struct Object *obj, s16 posIndex, s16 angleIndex) {
if (obj == NULL) { return; }
f32 translate[3];
s16 rotation[3];
@ -2104,6 +2131,7 @@ void obj_build_transform_from_pos_and_angle(struct Object *obj, s16 posIndex, s1
}
void obj_set_throw_matrix_from_transform(struct Object *obj) {
if (obj == NULL) { return; }
if (obj->oFlags & OBJ_FLAG_0020) {
obj_build_transform_from_pos_and_angle(obj, O_POS_INDEX, O_FACE_ANGLE_INDEX);
obj_apply_scale_to_transform(obj);
@ -2117,6 +2145,7 @@ void obj_set_throw_matrix_from_transform(struct Object *obj) {
}
void obj_build_transform_relative_to_parent(struct Object *obj) {
if (obj == NULL) { return; }
struct Object *parent = obj->parentObj;
obj_build_transform_from_pos_and_angle(obj, O_PARENT_RELATIVE_POS_INDEX, O_FACE_ANGLE_INDEX);
@ -2135,6 +2164,7 @@ void obj_build_transform_relative_to_parent(struct Object *obj) {
}
void obj_create_transform_from_self(struct Object *obj) {
if (obj == NULL) { return; }
obj->oFlags &= ~OBJ_FLAG_TRANSFORM_RELATIVE_TO_PARENT;
obj->oFlags |= OBJ_FLAG_SET_THROW_MATRIX_FROM_TRANSFORM;
@ -2219,6 +2249,7 @@ s32 cur_obj_follow_path(UNUSED s32 unusedArg) {
}
void chain_segment_init(struct ChainSegment *segment) {
if (segment == NULL) { return; }
segment->posX = 0.0f;
segment->posY = 0.0f;
segment->posZ = 0.0f;
@ -2238,17 +2269,20 @@ void obj_scale_random(struct Object *obj, f32 rangeLength, f32 minScale) {
}
void obj_translate_xyz_random(struct Object *obj, f32 rangeLength) {
if (obj == NULL) { return; }
obj->oPosX += random_float() * rangeLength - rangeLength * 0.5f;
obj->oPosY += random_float() * rangeLength - rangeLength * 0.5f;
obj->oPosZ += random_float() * rangeLength - rangeLength * 0.5f;
}
void obj_translate_xz_random(struct Object *obj, f32 rangeLength) {
if (obj == NULL) { return; }
obj->oPosX += random_float() * rangeLength - rangeLength * 0.5f;
obj->oPosZ += random_float() * rangeLength - rangeLength * 0.5f;
}
void obj_build_vel_from_transform(struct Object *a0) {
if (a0 == NULL) { return; }
f32 spC = a0->oUnkC0;
f32 sp8 = a0->oUnkBC;
f32 sp4 = a0->oForwardVel;
@ -2272,6 +2306,7 @@ s16 cur_obj_reflect_move_angle_off_wall(void) {
}
void cur_obj_spawn_particles(struct SpawnParticlesInfo *info) {
if (info == NULL) { return; }
struct Object *particle;
s32 i;
f32 scale;
@ -2309,6 +2344,7 @@ void cur_obj_spawn_particles(struct SpawnParticlesInfo *info) {
}
void obj_set_hitbox(struct Object *obj, struct ObjectHitbox *hitbox) {
if (obj == NULL || hitbox == NULL) { return; }
if (!(obj->oFlags & OBJ_FLAG_30)) {
obj->oFlags |= OBJ_FLAG_30;
@ -3018,6 +3054,7 @@ void cur_obj_shake_screen(s32 shake) {
}
s32 obj_attack_collided_from_other_object(struct Object *obj) {
if (obj == NULL) { return FALSE; }
s32 numCollidedObjs;
struct Object *other;
s32 touchedOtherObject = FALSE;
@ -3053,6 +3090,7 @@ s32 cur_obj_was_attacked_or_ground_pounded(void) {
}
void obj_copy_behavior_params(struct Object *dst, struct Object *src) {
if (dst == NULL || src == NULL) { return; }
dst->oBehParams = src->oBehParams;
dst->oBehParams2ndByte = src->oBehParams2ndByte;
}

View file

@ -4,16 +4,12 @@
#include "pc/djui/djui_chat_message.h"
#include "chat_commands.h"
#include "pc/network/ban_list.h"
#include "pc/network/moderator_list.h"
#include "pc/debuglog.h"
#include "level_table.h"
enum ChatConfirmCommand {
CCC_NONE,
CCC_KICK,
CCC_BAN,
CCC_PERMBAN,
};
extern int gIsModerator;
static enum ChatConfirmCommand sConfirming = CCC_NONE;
static u8 sConfirmPlayerIndex = 0;
@ -49,26 +45,38 @@ bool exec_chat_command(char* command) {
sConfirming = CCC_NONE;
if (ccc != CCC_NONE && strcmp("/confirm", command) == 0) {
if (gNetworkType == NT_SERVER && ccc == CCC_KICK) {
struct NetworkPlayer* np = &gNetworkPlayers[sConfirmPlayerIndex];
if (!np->connected) { return true; }
char message[256] = { 0 };
snprintf(message, 256, "\\#fff982\\Kicking '%s%s\\#fff982\\'!", network_get_player_text_color_string(np->localIndex), np->name);
djui_chat_message_create(message);
network_send_kick(np->localIndex, EKT_KICKED);
network_player_disconnected(np->localIndex);
return true;
if (gNetworkType == NT_SERVER || gIsModerator == 1) {
if (ccc == CCC_KICK) {
struct NetworkPlayer* np = &gNetworkPlayers[sConfirmPlayerIndex];
if (!np->connected) { return true; }
char message[256] = { 0 };
snprintf(message, 256, "\\#fff982\\Kicking '%s%s\\#fff982\\'!", network_get_player_text_color_string(np->localIndex), np->name);
djui_chat_message_create(message);
if (gNetworkType == NT_SERVER) {
network_send_kick(np->localIndex, EKT_KICKED);
network_player_disconnected(np->localIndex);
} else {
network_send_chat_command(np->globalIndex, CCC_KICK);
}
return true;
}
}
if (gNetworkType == NT_SERVER && ccc == CCC_BAN) {
struct NetworkPlayer* np = &gNetworkPlayers[sConfirmPlayerIndex];
if (!np->connected) { return true; }
char message[256] = { 0 };
snprintf(message, 256, "\\#fff982\\Banning '%s%s\\#fff982\\'!", network_get_player_text_color_string(np->localIndex), np->name);
djui_chat_message_create(message);
network_send_kick(np->localIndex, EKT_BANNED);
ban_list_add(gNetworkSystem->get_id_str(np->localIndex), false);
network_player_disconnected(np->localIndex);
return true;
if (gNetworkType == NT_SERVER || gIsModerator == 1) {
if (ccc == CCC_BAN) {
struct NetworkPlayer* np = &gNetworkPlayers[sConfirmPlayerIndex];
if (!np->connected) { return true; }
char message[256] = { 0 };
snprintf(message, 256, "\\#fff982\\Banning '%s%s\\#fff982\\'!", network_get_player_text_color_string(np->localIndex), np->name);
djui_chat_message_create(message);
if (gNetworkType == NT_SERVER) {
network_send_kick(np->localIndex, EKT_BANNED);
ban_list_add(gNetworkSystem->get_id_str(np->localIndex), false);
network_player_disconnected(np->localIndex);
} else {
network_send_chat_command(np->globalIndex, CCC_BAN);
}
return true;
}
}
if (gNetworkType == NT_SERVER && ccc == CCC_PERMBAN) {
struct NetworkPlayer* np = &gNetworkPlayers[sConfirmPlayerIndex];
@ -81,6 +89,16 @@ bool exec_chat_command(char* command) {
network_player_disconnected(np->localIndex);
return true;
}
if (gNetworkType == NT_SERVER && ccc == CCC_MODERATOR) {
struct NetworkPlayer* np = &gNetworkPlayers[sConfirmPlayerIndex];
if (!np->connected) { return true; }
char message[256] = { 0 };
snprintf(message, 256, "\\#fff982\\Adding '%s%s\\#fff982\\' as a Moderator!", network_get_player_text_color_string(np->localIndex), np->name);
djui_chat_message_create(message);
network_send_moderator(np->localIndex);
moderator_list_add(gNetworkSystem->get_id_str(np->localIndex), true);
return true;
}
}
if (strcmp("/players", command) == 0) {
@ -102,8 +120,8 @@ bool exec_chat_command(char* command) {
}
if (str_starts_with("/kick ", command)) {
if (gNetworkType != NT_SERVER) {
djui_chat_message_create("Only the server can use this command.");
if (gNetworkType != NT_SERVER && gIsModerator == 0) {
djui_chat_message_create("You do not have permission to use this command.");
return true;
}
@ -129,8 +147,8 @@ bool exec_chat_command(char* command) {
}
if (str_starts_with("/ban ", command)) {
if (gNetworkType != NT_SERVER) {
djui_chat_message_create("Only the server can use this command.");
if (gNetworkType != NT_SERVER && gIsModerator == 0) {
djui_chat_message_create("You do not have permission to use this command.");
return true;
}
@ -156,8 +174,8 @@ bool exec_chat_command(char* command) {
}
if (str_starts_with("/permban ", command)) {
if (gNetworkType != NT_SERVER) {
djui_chat_message_create("Only the server can use this command.");
if (gNetworkType != NT_SERVER && gIsModerator == 0) {
djui_chat_message_create("You do not have permission to use this command.");
return true;
}
@ -182,6 +200,32 @@ bool exec_chat_command(char* command) {
return true;
}
if (str_starts_with("/moderator ", command)) {
if (gNetworkType != NT_SERVER) {
djui_chat_message_create("Only the server can use this command.");
return true;
}
struct NetworkPlayer* np = chat_get_network_player(&command[11]);
if (np == NULL) {
djui_chat_message_create("Could not find player.");
return true;
}
if (np->localIndex == 0) {
djui_chat_message_create("Can not make yourself a moderator.");
return true;
}
char message[256] = { 0 };
snprintf(message, 256, "\\#fff982\\Are you sure you want to make '%s%s\\#fff982\\' a moderator?\nType '\\#a0ffa0\\/confirm\\#fff982\\' to moderate.", network_get_player_text_color_string(np->localIndex), np->name);
djui_chat_message_create(message);
sConfirming = CCC_MODERATOR;
sConfirmPlayerIndex = np->localIndex;
return true;
}
#if defined(DEBUG) && defined(DEVELOPMENT)
if (gNetworkSystem == &gNetworkSystemSocket && str_starts_with("/warp ", command)) {
static const struct { const char *name; s32 num; } sLevelNumByName[] = {
@ -269,10 +313,11 @@ bool exec_chat_command(char* command) {
void display_chat_commands(void) {
djui_chat_message_create("/players - List all players and their IDs");
if (gNetworkType == NT_SERVER) {
if (gNetworkType == NT_SERVER || gIsModerator == 1) {
djui_chat_message_create("/kick [NAME|ID] - Kick this player from the current game");
djui_chat_message_create("/ban [NAME|ID] - Ban this player from the current game");
djui_chat_message_create("/permban [NAME|ID] - Ban this player from any game you host");
djui_chat_message_create("/moderator [NAME|ID] - Make this player able to use commands like /kick, /ban, /permban on any game you host");
}
#if defined(DEBUG) && defined(DEVELOPMENT)
djui_chat_message_create("/warp [LEVEL] [AREA] [ACT] - Level can be either a numeric value or a shorthand name");

View file

@ -16,6 +16,8 @@
#include "pc/mods/mods.h"
#include "pc/network/ban_list.h"
#include "pc/crash_handler.h"
#include "pc/network/moderator_list.h"
#define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0]))
@ -258,6 +260,19 @@ static void ban_write(FILE* file) {
}
}
static void moderator_read(char** tokens, UNUSED int numTokens) {
moderator_list_add(tokens[1], true);
}
static void moderator_write(FILE* file) {
for (unsigned int i = 0; i < gModeratorCount; i++) {
if (gModeratorAddresses == NULL) { break; }
if (gModeratorAddresses[i] == NULL) { continue; }
if (!gModerator[i]) { continue; }
fprintf(file, "%s %s\n", "moderator:", gModeratorAddresses[i]);
}
}
static void dynos_pack_read(char** tokens, int numTokens) {
if (numTokens < 3) { return; }
char fullPackName[256] = { 0 };
@ -290,6 +305,7 @@ static void dynos_pack_write(FILE* file) {
static const struct FunctionConfigOption functionOptions[] = {
{ .name = "enable-mod:", .read = enable_mod_read, .write = enable_mod_write },
{ .name = "ban:", .read = ban_read, .write = ban_write },
{ .name = "moderator:", .read = moderator_read, .write = moderator_write },
{ .name = "dynos-pack:", .read = dynos_pack_read, .write = dynos_pack_write },
};

View file

@ -16,7 +16,7 @@ void djui_panel_join_message_error(char* message) {
}
void djui_panel_join_message_cancel(struct DjuiBase* caller) {
network_shutdown(true);
network_shutdown(true, false);
djui_panel_menu_back(caller);
}

View file

@ -1,6 +1,7 @@
#include "djui.h"
#include "pc/cheats.h"
#include "src/pc/pc_main.h"
#include "src/pc/network/network.h"
bool gDjuiPanelPauseCreated = false;
@ -9,13 +10,13 @@ static void djui_panel_pause_resume(UNUSED struct DjuiBase* caller) {
}
static void djui_panel_pause_quit_yes(UNUSED struct DjuiBase* caller) {
game_exit();
network_shutdown(true, false);
}
static void djui_panel_pause_quit(struct DjuiBase* caller) {
djui_panel_confirm_create(caller,
"\\#ff0800\\Q\\#1be700\\U\\#00b3ff\\I\\#ffef00\\T",
"Are you sure you want to quit?",
"Are you sure you want to disconnect?",
djui_panel_pause_quit_yes);
}
@ -47,7 +48,7 @@ void djui_panel_pause_create(struct DjuiBase* caller) {
djui_base_set_size(&button2->base, 1.0f, 64);
djui_interactable_hook_click(&button2->base, djui_panel_pause_resume);
struct DjuiButton* button3 = djui_button_create(&body->base, "Quit");
struct DjuiButton* button3 = djui_button_create(&body->base, "Disconnect");
djui_base_set_size_type(&button3->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE);
djui_base_set_size(&button3->base, 1.0f, 64);
djui_interactable_hook_click(&button3->base, djui_panel_pause_quit);

View file

@ -68,30 +68,25 @@ void djui_popup_update(void) {
struct DjuiPopupList* next = node->next;
djui_base_set_location(&node->popup->base, 4, y);
y += node->popup->base.height.value + 4;
if (gNetworkType != NT_NONE && gNetworkPlayerLocal != NULL) {
f32 elapsed = (clock_elapsed() - node->createTime);
f32 elapsed = (clock_elapsed() - node->createTime);
// fade out
f32 alpha = fmin(DJUI_POPUP_LIFETIME - elapsed, 1.0f);
alpha *= alpha;
if (elapsed > DJUI_POPUP_LIFETIME) { alpha = 0; }
djui_base_set_color(&node->popup->base, 0, 0, 0, 220 * alpha);
djui_base_set_border_color(&node->popup->base, 0, 0, 0, 180 * alpha);
djui_base_set_color(&node->popup->text->base, 220, 220, 220, 255 * alpha);
djui_text_set_drop_shadow(node->popup->text, 0, 0, 0, 64 * alpha);
// fade out
f32 alpha = fmin(DJUI_POPUP_LIFETIME - elapsed, 1.0f);
alpha *= alpha;
if (elapsed > DJUI_POPUP_LIFETIME) { alpha = 0; }
djui_base_set_color(&node->popup->base, 0, 0, 0, 220 * alpha);
djui_base_set_border_color(&node->popup->base, 0, 0, 0, 180 * alpha);
djui_base_set_color(&node->popup->text->base, 220, 220, 220, 255 * alpha);
djui_text_set_drop_shadow(node->popup->text, 0, 0, 0, 64 * alpha);
// remove/deallocate popup
if (alpha == 0) {
if (last != NULL) { last->next = next; }
if (node == sPopupListHead) { sPopupListHead = next; }
djui_base_destroy(&node->popup->base);
free(node);
node = next;
continue;
}
} else {
// prevent popups from fading out when we're not connected
node->createTime = clock_elapsed();
// remove/deallocate popup
if (alpha == 0) {
if (last != NULL) { last->next = next; }
if (node == sPopupListHead) { sPopupListHead = next; }
djui_base_destroy(&node->popup->base);
free(node);
node = next;
continue;
}
// iterate

View file

@ -180,8 +180,10 @@ void smlua_update(void) {
void smlua_shutdown(void) {
smlua_text_utils_reset_all();
smlua_audio_utils_reset_all();
audio_custom_shutdown();
smlua_cobject_allowlist_shutdown();
smlua_cpointer_allowlist_shutdown();
smlua_clear_hooks();
lua_State* L = gLuaState;
if (L != NULL) {
lua_close(L);

View file

@ -977,7 +977,7 @@ int smlua_hook_on_sync_table_change(lua_State* L) {
// misc //
//////////
static void smlua_clear_hooks(void) {
void smlua_clear_hooks(void) {
for (int i = 0; i < HOOK_MAX; i++) {
struct LuaHookedEvent* hooked = &sHookedEvents[i];
for (int j = 0; j < hooked->count; j++) {

View file

@ -88,6 +88,7 @@ u32 smlua_get_action_interaction_type(struct MarioState* m);
bool smlua_call_chat_command_hook(char* command);
void smlua_display_chat_commands(void);
void smlua_clear_hooks(void);
void smlua_bind_hooks(void);
#endif

View file

@ -163,6 +163,22 @@ static bool audio_sanity_check(struct BassAudio* audio, bool isStream, const cha
}
struct BassAudio* audio_load_internal(const char* filename, bool isStream) {
// check file type
bool validFileType = false;
const char* fileTypes[] = { ".mp3", ".aiff", ".ogg", NULL };
const char** ft = fileTypes;
while (*ft != NULL) {
if (str_ends_with((char*)filename, (char*)*ft)) {
validFileType = true;
break;
}
ft++;
}
if (!validFileType) {
LOG_LUA_LINE("Tried to load audio file with invalid file type: %s", filename);
return NULL;
}
// find mod file in mod list
bool foundModFile = false;
struct ModFile* modFile = NULL;

View file

@ -289,7 +289,7 @@ static bool mod_load_files(struct Mod* mod, char* modName, char* fullPath) {
// deal with sound directory
{
const char* fileTypes[] = { ".m64", ".mp3", ".aiff", ".ogg", NULL };
if (!mod_load_files_dir(mod, fullPath, "levels", fileTypes)) { return false; }
if (!mod_load_files_dir(mod, fullPath, "sound", fileTypes)) { return false; }
}
return true;

View file

@ -102,27 +102,16 @@ struct ModCacheEntry* mod_cache_get_from_hash(u8* dataHash) {
return NULL;
}
static bool mod_cache_has_path(const char* path) {
if (path == NULL || strlen(path) == 0) { return NULL; }
struct ModCacheEntry* node = sModCacheHead;
while (node != NULL) {
struct ModCacheEntry* next = node->next;
if (!strcmp(node->path, path)) {
return true;
}
node = next;
}
return false;
}
struct ModCacheEntry* mod_cache_get_from_path(const char* path) {
struct ModCacheEntry* mod_cache_get_from_path(const char* path, bool validate) {
if (path == NULL || strlen(path) == 0) { return NULL; }
struct ModCacheEntry* node = sModCacheHead;
struct ModCacheEntry* prev = NULL;
while (node != NULL) {
struct ModCacheEntry* next = node->next;
if (!strcmp(node->path, path)) {
if (mod_cache_is_valid(node)) {
if (!validate) {
return node;
} else if (mod_cache_is_valid(node)) {
return node;
} else {
mod_cache_remove_node(node, prev);
@ -220,7 +209,10 @@ void mod_cache_add(struct Mod* mod, struct ModFile* file, bool useFilePath) {
file->cachedPath = strdup(modFilePath);
// if we already have the filepath, don't MD5 it again
if (useFilePath && mod_cache_has_path(file->cachedPath)) {
struct ModCacheEntry* entry = mod_cache_get_from_path(file->cachedPath, false);
if (useFilePath && entry) {
memcpy(file->dataHash, entry->dataHash, 16);
mod_cache_add_internal(file->dataHash, 0, strdup(file->cachedPath));
return;
}

View file

@ -12,7 +12,7 @@ struct ModCacheEntry {
void mod_cache_shutdown(void);
struct ModCacheEntry* mod_cache_get_from_hash(u8* dataHash);
struct ModCacheEntry* mod_cache_get_from_path(const char* path);
struct ModCacheEntry* mod_cache_get_from_path(const char* path, bool validate);
void mod_cache_add(struct Mod* mod, struct ModFile* modFile, bool useFilePath);
void mod_cache_load(void);
void mod_cache_save(void);

View file

@ -0,0 +1,47 @@
#include <stdlib.h>
#include <string.h>
#include "PR/ultratypes.h"
#include "moderator_list.h"
#include "pc/debuglog.h"
char** gModeratorAddresses = NULL;
bool* gModerator = NULL;
u16 gModeratorCount = 0;
void moderator_list_add(char* address, bool perm) {
u16 index = gModeratorCount++;
if (gModeratorAddresses == NULL) {
gModeratorAddresses = malloc(sizeof(char*) * gModeratorCount);
gModerator = malloc(sizeof(bool) * gModeratorCount);
} else {
gModeratorAddresses = realloc(gModeratorAddresses, sizeof(char*) * gModeratorCount);
assert(gModeratorAddresses != NULL);
gModerator = realloc(gModerator, sizeof(bool) * gModeratorCount);
assert(gModerator != NULL);
}
if (gModeratorAddresses == NULL) {
LOG_ERROR("Failed to allocate gModeratorAddresses");
return;
}
if (gModerator == NULL) {
LOG_ERROR("Failed to allocate gModerator");
return;
}
gModeratorAddresses[index] = strdup(address);
gModerator[index] = perm;
}
bool moderator_list_contains(char* address) {
if (gModeratorAddresses == NULL || address == NULL) {
return false;
}
for (s32 i = 0; i < gModeratorCount; i++) {
if (gModeratorAddresses[i] == NULL) { continue; }
if (strcmp(address, gModeratorAddresses[i]) == 0) {
return true;
}
}
return false;
}

View file

@ -0,0 +1,13 @@
#ifndef MODERATOR_LIST_H
#define MODERATOR_LIST_H
#include <stdbool.h>
extern char** gModeratorAddresses;
extern bool* gModerator;
extern u16 gModeratorCount;
void moderator_list_add(char* address, bool perm);
bool moderator_list_contains(char* address);
#endif

View file

@ -192,7 +192,7 @@ void network_send_to(u8 localIndex, struct Packet* p) {
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);
// SOFT_ASSERT(false); - Crash?
return;
}
@ -418,7 +418,7 @@ void network_register_mod(char* modName) {
string_linked_list_append(&gRegisteredMods, modName);
}
void network_shutdown(bool sendLeaving) {
void network_shutdown(bool sendLeaving, bool exiting) {
if (gDjuiChatBox != NULL) {
djui_base_destroy(&gDjuiChatBox->base);
gDjuiChatBox = NULL;
@ -440,4 +440,26 @@ void network_shutdown(bool sendLeaving) {
}
gNetworkType = NT_NONE;
if (exiting) { return; }
// reset other stuff
extern u8* gOverrideEeprom;
gOverrideEeprom = NULL;
dynos_mod_shutdown();
mods_clear(&gActiveMods);
mods_clear(&gRemoteMods);
smlua_shutdown();
extern s16 gChangeLevel;
gChangeLevel = LEVEL_CASTLE_GROUNDS;
extern s16 gMenuMode;
gMenuMode = -1;
djui_panel_shutdown();
extern bool gDjuiInMainMenu;
if (!gDjuiInMainMenu) {
gDjuiInMainMenu = true;
djui_panel_main_create(NULL);
}
}

View file

@ -130,6 +130,6 @@ void network_receive(u8 localIndex, void* addr, u8* data, u16 dataLength);
void* network_duplicate_address(u8 localIndex);
void network_update(void);
void network_register_mod(char* modName);
void network_shutdown(bool sendLeaving);
void network_shutdown(bool sendLeaving, bool exiting);
#endif

View file

@ -157,7 +157,7 @@ void network_player_update(void) {
#ifndef DEVELOPMENT
if (elapsed > NETWORK_PLAYER_TIMEOUT * 1.5f) {
LOG_INFO("dropping due to no server connectivity");
network_shutdown(false);
network_shutdown(false, false);
}
#endif
@ -264,6 +264,7 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 mode
smlua_call_event_hooks_mario_param(HOOK_ON_PLAYER_CONNECTED, &gMarioStates[localIndex]);
return localIndex;
}
@ -273,7 +274,7 @@ u8 network_player_disconnected(u8 globalIndex) {
LOG_ERROR("player disconnected, but it's local.. this shouldn't happen!");
return UNKNOWN_GLOBAL_INDEX;
} else {
network_shutdown(true);
network_shutdown(true, false);
}
}

View file

@ -51,6 +51,8 @@ void packet_process(struct Packet* p) {
case PACKET_JOIN: network_receive_join(p); break;
case PACKET_CHAT: network_receive_chat(p); break;
case PACKET_KICK: network_receive_kick(p); break;
case PACKET_COMMAND: network_recieve_chat_command(p); break;
case PACKET_MODERATOR: network_recieve_moderator(); break;
case PACKET_KEEP_ALIVE: network_receive_keep_alive(p); break;
case PACKET_LEAVING: network_receive_leaving(p); break;
case PACKET_SAVE_FILE: network_receive_save_file(p); break;

View file

@ -70,6 +70,9 @@ enum PacketType {
PACKET_REQUEST_FAILED,
PACKET_LUA_CUSTOM,
PACKET_COMMAND,
PACKET_MODERATOR,
///
PACKET_CUSTOM = 255,
@ -113,6 +116,14 @@ enum KickReasonType {
EKT_BANNED,
};
enum ChatConfirmCommand {
CCC_NONE,
CCC_KICK,
CCC_BAN,
CCC_PERMBAN,
CCC_MODERATOR,
};
struct LSTNetworkType {
enum {
LST_NETWORK_TYPE_INTEGER,
@ -230,6 +241,14 @@ void network_receive_chat(struct Packet* p);
void network_send_kick(u8 localIndex, enum KickReasonType kickReason);
void network_receive_kick(struct Packet* p);
// packet_command_mod.c
void network_send_chat_command(u8 localIndex, enum ChatConfirmCommand CCC);
void network_recieve_chat_command(struct Packet* p);
// packet_moderator.c
void network_send_moderator(u8 localIndex);
void network_recieve_moderator(void);
// packet_keep_alive.c
void network_send_keep_alive(u8 localIndex);
void network_receive_keep_alive(struct Packet* p);

View file

@ -0,0 +1,61 @@
#include <stdio.h>
#include "../network.h"
#include "pc/djui/djui_chat_message.h"
#include "pc/network/ban_list.h"
#include "pc/network/moderator_list.h"
int gIsModerator;
void network_send_chat_command(u8 globalIndex, enum ChatConfirmCommand ccc) {
if (gIsModerator == 1) {
u8 cccType = ccc;
struct Packet p = { 0 };
packet_init(&p, PACKET_COMMAND, false, PLMT_NONE);
packet_write(&p, &globalIndex, sizeof(u8));
packet_write(&p, &cccType, sizeof(u8));
network_send_to(gNetworkPlayerServer->localIndex, &p);
}
}
void network_recieve_chat_command(struct Packet* p) {
if (!moderator_list_contains(gNetworkSystem->get_id_str(p->localIndex))) {
return;
}
enum ChatConfirmCommand CCC;
u8 player;
packet_read(p, &player, sizeof(u8));
packet_read(p, &CCC, sizeof(u8));
if (gNetworkType == NT_SERVER && CCC == CCC_KICK) {
struct NetworkPlayer* np = &gNetworkPlayers[player];
if (!np->connected) { return; }
network_send_kick(np->localIndex, EKT_KICKED);
network_player_disconnected(np->localIndex);
char message[256] = { 0 };
snprintf(message, 256, "\\#fff982\\Kicked '%s%s\\#fff982\\'!", network_get_player_text_color_string(np->localIndex), np->name);
djui_chat_message_create(message);
}
if (gNetworkType == NT_SERVER && CCC == CCC_BAN) {
struct NetworkPlayer* np = &gNetworkPlayers[player];
if (!np->connected) { return; }
network_send_kick(np->localIndex, EKT_BANNED);
ban_list_add(gNetworkSystem->get_id_str(np->localIndex), false);
network_player_disconnected(np->localIndex);
char message[256] = { 0 };
snprintf(message, 256, "\\#fff982\\Banned '%s%s\\#fff982\\'!", network_get_player_text_color_string(np->localIndex), np->name);
djui_chat_message_create(message);
}
}
void network_send_moderator(u8 localIndex) {
struct Packet p = { 0 };
packet_init(&p, PACKET_MODERATOR, false, PLMT_NONE);
network_send_to(localIndex, &p);
}
void network_recieve_moderator(void) {
if (gIsModerator == 1) {
return;
}
gIsModerator = 1;
djui_chat_message_create("\\#fff982\\You are now a Moderator.");
}

View file

@ -153,7 +153,7 @@ void network_receive_join(struct Packet* p) {
packet_read(p, &remoteVersion, sizeof(u8) * MAX_VERSION_LENGTH);
LOG_INFO("server has version: %s", version);
if (memcmp(version, remoteVersion, MAX_VERSION_LENGTH) != 0) {
network_shutdown(true);
network_shutdown(true, false);
LOG_ERROR("version mismatch");
char mismatchMessage[256] = { 0 };
snprintf(mismatchMessage, 256, "\\#ffa0a0\\Error:\\#c8c8c8\\ Version mismatch.\n\nYour version: \\#a0a0ff\\%s\\#c8c8c8\\\nTheir version: \\#a0a0ff\\%s\\#c8c8c8\\\n\nSomeone is out of date!\n", version, remoteVersion);
@ -186,7 +186,7 @@ void network_receive_join(struct Packet* p) {
}
if (string_linked_list_mismatch(&gRegisteredMods, &head)) {
network_shutdown(true);
network_shutdown(true, false);
struct StringBuilder* builder = string_builder_create(512);
string_builder_append(builder, "\\#ffa0a0\\Error:\\#c8c8c8\\ mods don't match.\n\n");

View file

@ -32,5 +32,5 @@ void network_receive_kick(struct Packet* p) {
case EKT_BANNED: djui_panel_join_message_error("\\#ffa0a0\\Error:\\#c8c8c8\\ The server banned you."); break;
default: djui_panel_join_message_error("\\#ffa0a0\\Error:\\#c8c8c8\\ Host has closed the connection."); break;
}
network_shutdown(false);
network_shutdown(false, false);
}

View file

@ -121,7 +121,7 @@ void network_receive_mod_list(struct Packet* p) {
packet_read(p, &remoteVersion, sizeof(u8) * MAX_VERSION_LENGTH);
LOG_INFO("server has version: %s", version);
if (memcmp(version, remoteVersion, MAX_VERSION_LENGTH) != 0) {
network_shutdown(true);
network_shutdown(true, false);
LOG_ERROR("version mismatch");
char mismatchMessage[256] = { 0 };
snprintf(mismatchMessage, 256, "\\#ffa0a0\\Error:\\#c8c8c8\\ Version mismatch.\n\nYour version: \\#a0a0ff\\%s\\#c8c8c8\\\nTheir version: \\#a0a0ff\\%s\\#c8c8c8\\\n\nSomeone is out of date!\n", version, remoteVersion);
@ -205,7 +205,7 @@ void network_receive_mod_list_entry(struct Packet* p) {
// sanity check mod size
if (mod->size >= MAX_MOD_SIZE) {
djui_popup_create("Server had too large of a mod.\nQuitting.", 4);
network_shutdown(false);
network_shutdown(false, false);
return;
}

View file

@ -5,6 +5,7 @@
#include "src/game/behavior_actions.h"
#include "pc/debuglog.h"
#include "pc/configfile.h"
#include "pc/network/moderator_list.h"
static void network_send_to_network_players(u8 sendToLocalIndex) {
SOFT_ASSERT(gNetworkType == NT_SERVER);
@ -57,6 +58,11 @@ void network_receive_network_players_request(struct Packet* p) {
return;
}
network_send_to_network_players(localIndex);
if (moderator_list_contains(gNetworkSystem->get_id_str(p->localIndex))) {
LOG_INFO("sending moderator packet to localIndex: %d", p->localIndex);
network_send_moderator(p->localIndex);
}
}
void network_send_network_players(u8 exceptLocalIndex) {

View file

@ -4,8 +4,8 @@
#include "pc/debuglog.h"
#include "pc/djui/djui.h"
static SOCKET curSocket = INVALID_SOCKET;
static struct sockaddr_in addr[MAX_PLAYERS] = { 0 };
static SOCKET sCurSocket = INVALID_SOCKET;
static struct sockaddr_in sAddr[MAX_PLAYERS] = { 0 };
static int socket_bind(SOCKET socket, unsigned int port) {
struct sockaddr_in rxAddr;
@ -40,7 +40,7 @@ static int socket_receive(SOCKET socket, struct sockaddr_in* rxAddr, u8* buffer,
int rc = recvfrom(socket, (char*)buffer, bufferLength, 0, (struct sockaddr*)rxAddr, &rxAddrSize);
for (int i = 1; i < MAX_PLAYERS; i++) {
if (memcmp(rxAddr, &addr[i], sizeof(struct sockaddr_in)) == 0) {
if (memcmp(rxAddr, &sAddr[i], sizeof(struct sockaddr_in)) == 0) {
*localIndex = i;
break;
}
@ -65,21 +65,32 @@ static bool ns_socket_initialize(enum NetworkType networkType) {
if (port == 0) { port = DEFAULT_PORT; }
// create a receiver socket to receive datagrams
curSocket = socket_initialize();
if (curSocket == INVALID_SOCKET) { return false; }
sCurSocket = socket_initialize();
if (sCurSocket == INVALID_SOCKET) { return false; }
// connect
if (networkType == NT_SERVER) {
int reuse = 1;
if (setsockopt(sCurSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) < 0) {
LOG_ERROR("setsockopt(SO_REUSEADDR) failed");
}
#ifdef SO_REUSEPORT
if (setsockopt(sCurSocket, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse)) < 0) {
LOG_ERROR("setsockopt(SO_REUSEPORT) failed");
}
#endif
// bind the socket to any address and the specified port.
int rc = socket_bind(curSocket, port);
int rc = socket_bind(sCurSocket, port);
if (rc != NO_ERROR) { return false; }
LOG_INFO("bound to port %u", port);
} else {
// save the port to send to
addr[0].sin_family = AF_INET;
addr[0].sin_port = htons(port);
sAddr[0].sin_family = AF_INET;
sAddr[0].sin_port = htons(port);
domain_resolution();
addr[0].sin_addr.s_addr = inet_addr(configJoinIp);
sAddr[0].sin_addr.s_addr = inet_addr(configJoinIp);
LOG_INFO("connecting to %s %u", configJoinIp, port);
}
@ -109,27 +120,27 @@ static s64 ns_socket_get_id(UNUSED u8 localId) {
static char* ns_socket_get_id_str(u8 localId) {
if (localId == UNKNOWN_LOCAL_INDEX) { localId = 0; }
static char id_str[INET_ADDRSTRLEN] = { 0 };
snprintf(id_str, INET_ADDRSTRLEN, "%s", inet_ntoa(addr[localId].sin_addr));
snprintf(id_str, INET_ADDRSTRLEN, "%s", inet_ntoa(sAddr[localId].sin_addr));
return id_str;
}
static void ns_socket_save_id(u8 localId, UNUSED s64 networkId) {
SOFT_ASSERT(localId > 0);
SOFT_ASSERT(localId < MAX_PLAYERS);
addr[localId] = addr[0];
sAddr[localId] = sAddr[0];
LOG_INFO("saved addr for id %d", localId);
}
static void ns_socket_clear_id(u8 localId) {
if (localId == 0) { return; }
SOFT_ASSERT(localId < MAX_PLAYERS);
memset(&addr[localId], 0, sizeof(struct sockaddr_in));
memset(&sAddr[localId], 0, sizeof(struct sockaddr_in));
LOG_INFO("cleared addr for id %d", localId);
}
static void* ns_socket_dup_addr(u8 localIndex) {
void* address = malloc(sizeof(struct sockaddr_in));
memcpy(address, &addr[localIndex], sizeof(struct sockaddr_in));
memcpy(address, &sAddr[localIndex], sizeof(struct sockaddr_in));
return address;
}
@ -144,10 +155,10 @@ static void ns_socket_update(void) {
u8 data[PACKET_LENGTH + 1];
u16 dataLength = 0;
u8 localIndex = UNKNOWN_LOCAL_INDEX;
int rc = socket_receive(curSocket, &addr[0], data, PACKET_LENGTH + 1, &dataLength, &localIndex);
int rc = socket_receive(sCurSocket, &sAddr[0], data, PACKET_LENGTH + 1, &dataLength, &localIndex);
SOFT_ASSERT(dataLength < PACKET_LENGTH);
if (rc != NO_ERROR) { break; }
network_receive(localIndex, &addr[0], data, dataLength);
network_receive(localIndex, &sAddr[0], data, dataLength);
} while (true);
}
@ -157,10 +168,10 @@ static int ns_socket_send(u8 localIndex, void* address, u8* data, u16 dataLength
if (gNetworkType == NT_CLIENT && gNetworkPlayers[localIndex].type != NPT_SERVER) { return SOCKET_ERROR; }
}
struct sockaddr_in* userAddr = &addr[localIndex];
struct sockaddr_in* userAddr = &sAddr[localIndex];
if (localIndex == 0 && address != NULL) { userAddr = (struct sockaddr_in*)address; }
int rc = socket_send(curSocket, userAddr, data, dataLength);
int rc = socket_send(sCurSocket, userAddr, data, dataLength);
if (rc) {
LOG_ERROR(" localIndex: %d, packetType: %d, dataLength: %d", localIndex, data[0], dataLength);
}
@ -168,8 +179,11 @@ static int ns_socket_send(u8 localIndex, void* address, u8* data, u16 dataLength
}
static void ns_socket_shutdown(void) {
socket_shutdown(curSocket);
curSocket = INVALID_SOCKET;
socket_shutdown(sCurSocket);
sCurSocket = INVALID_SOCKET;
for (u16 i = 0; i < MAX_PLAYERS; i++) {
memset(&sAddr[i], 0, sizeof(struct sockaddr_in));
}
LOG_INFO("shutdown");
}

View file

@ -3,11 +3,7 @@
#include "types.h"
static char sVersionString[MAX_VERSION_LENGTH] = { 0 };
#ifdef UNSTABLE_BRANCH
#define VERSION_TEXT "unst "
#else
#define VERSION_TEXT "beta "
#endif
char* get_version(void) {
snprintf(sVersionString, MAX_VERSION_LENGTH, "%s%d.%d", VERSION_TEXT, VERSION_NUMBER, MINOR_VERSION_NUMBER);

View file

@ -1,7 +1,6 @@
#ifndef VERSION_H
#define VERSION_H
#define UNSTABLE_BRANCH
#define VERSION_NUMBER 27
#define MINOR_VERSION_NUMBER 0

View file

@ -258,7 +258,7 @@ void game_deinit(void) {
audio_custom_shutdown();
audio_shutdown();
gfx_shutdown();
network_shutdown(true);
network_shutdown(true, true);
smlua_shutdown();
mods_shutdown();
inited = false;