Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
sapphonie committed Jul 26, 2023
2 parents 01f6cb3 + 531b053 commit 0638890
Show file tree
Hide file tree
Showing 17 changed files with 572 additions and 22 deletions.
104 changes: 104 additions & 0 deletions .github/workflows/mocktest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
name: hl2sdk-mock tests
on:
push:
branches:
- master
- '[0-9]+.[0-9]+-dev'
pull_request:
branches:
- master
- '[0-9]+.[0-9]+-dev'
jobs:
mock:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
name: Clone sourcemod
with:
submodules: recursive
path: sourcemod

- uses: actions/checkout@v3
name: Clone metamod-source
with:
repository: alliedmodders/metamod-source
submodules: recursive
path: metamod-source

- uses: actions/checkout@v3
name: Clone hl2sdk-mock
with:
repository: alliedmodders/hl2sdk-mock
submodules: recursive
path: hl2sdk-mock

- uses: actions/setup-python@v4
name: Setup Python 3.10
with:
python-version: "3.10"

- name: Install AMBuild
run: |
python -m pip install --upgrade pip setuptools wheel
pip install git+https://github.com/alliedmodders/ambuild
- name: Build MetaMod:Source
working-directory: metamod-source
run: |
python configure.py --enable-optimize --sdks=mock --targets=x86_64
ambuild objdir
- name: Build SourceMod
working-directory: sourcemod
run: |
python configure.py --no-mysql --enable-optimize --sdks=mock --targets=x86_64
ambuild objdir
- name: Build hl2sdk-mock
working-directory: hl2sdk-mock
run: |
python configure.py --enable-optimize --targets=x86_64
ambuild objdir
- name: Setup gamedir
working-directory: hl2sdk-mock
shell: bash
run: |
mkdir ../gamedir
./build_gamedir.sh ../gamedir ../metamod-source/objdir/package
./build_gamedir.sh ../gamedir ../sourcemod/objdir/package
- name: Compile testsuite
working-directory: hl2sdk-mock
shell: bash
run: |
mkdir ../gamedir/addons/sourcemod/plugins/optional
for f in ../sourcemod/plugins/testsuite/mock/*.sp; do
echo "Compiling $(basename $f)"
../gamedir/addons/sourcemod/scripting/spcomp64 -i ../gamedir/addons/sourcemod/scripting/include -o "../gamedir/addons/sourcemod/plugins/optional/$(basename $f .sp).smx" -E "$f"
done
- name: Test
working-directory: hl2sdk-mock
shell: bash
run: |
for f in ../gamedir/addons/sourcemod/plugins/optional/*.smx; do
echo "==================================="
echo "Running $(basename $f)..."
echo "==================================="
timeout 60 ./objdir/dist/x86_64/srcds -game_dir ../gamedir +map de_thunder -command "sm plugins load optional/$(basename $f)" -run -run-ticks 20 |
{
failed=0
while IFS= read -r line; do
echo "$line"
if [[ "$line" == *"FAIL"* ]]; then
failed=1
fi
done
if [ "$failed" = "1" ]; then
echo "$(basename $f) failed."
exit 1
fi
}
done
21 changes: 20 additions & 1 deletion core/NextMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ bool NextMapManager::SetNextMap(const char *map)
return true;
}

static char g_nextMap[PLATFORM_MAX_PATH];

#if SOURCE_ENGINE != SE_DARKMESSIAH
void NextMapManager::HookChangeLevel(const char *map, const char *unknown)
#else
Expand All @@ -122,8 +124,16 @@ void NextMapManager::HookChangeLevel(const char *map, const char *unknown, const
}

const char *newmap = sm_nextmap.GetString();
if (newmap[0] != '\0') {
ke::SafeStrcpy(g_nextMap, sizeof(g_nextMap), newmap);
newmap = g_nextMap;

// Clear the value so that if the map load fails later we don't get stuck in a loop.
// This might cause us to go off-cycle for a map, but nextmap will get us back on track.
sm_nextmap.SetValue("");
}

if (newmap[0] == 0 || !g_HL2.IsMapValid(newmap))
if (newmap[0] == '\0' || !g_HL2.IsMapValid(newmap))
{
RETURN_META(MRES_IGNORED);
}
Expand All @@ -142,6 +152,15 @@ void NextMapManager::HookChangeLevel(const char *map, const char *unknown, const

void NextMapManager::OnSourceModLevelChange( const char *mapName )
{
// If we were controlling the map change, reset sm_nextmap to be the name of the map we successfully changed to.
// This maintains an old API contract on the plugin side. We use the real map name even if it was different from
// the expected map name as if the expected map failed to load we let the game take over instead, but the nextmap
// plugin compares the sm_nextmap value to the current map to decide if it should advance the mapcycle.
if (g_nextMap[0] != '\0') {
sm_nextmap.SetValue(mapName);
g_nextMap[0] = '\0';
}

/* Skip the first 'mapchange' when the server starts up */
if (m_tempChangeInfo.startTime != 0)
{
Expand Down
46 changes: 46 additions & 0 deletions core/logic/smn_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <iomanip>
#include <sstream>
#include <list>
#include "common_logic.h"
#include "Logger.h"
Expand Down Expand Up @@ -273,6 +275,49 @@ static cell_t FormatTime(IPluginContext *pContext, const cell_t *params)
return 1;
}

static int ParseTime(IPluginContext *pContext, const cell_t *params)
{
char *datetime;
char *format;
pContext->LocalToStringNULL(params[1], &datetime);
pContext->LocalToStringNULL(params[2], &format);

if (format == NULL)
{
format = const_cast<char *>(bridge->GetCvarString(g_datetime_format));
}
else if (!format[0])
{
return pContext->ThrowNativeError("Time format string cannot be empty.");
}
if (!datetime || !datetime[0])
{
return pContext->ThrowNativeError("Date/time string cannot be empty.");
}

// https://stackoverflow.com/a/33542189
std::tm t{};
std::istringstream input(datetime);

auto previousLocale = input.imbue(std::locale::classic());
input >> std::get_time(&t, format);
bool failed = input.fail();
input.imbue(previousLocale);

if (failed)
{
return pContext->ThrowNativeError("Invalid date/time string or time format.");
}

#if defined PLATFORM_WINDOWS
return _mkgmtime(&t);
#elif defined PLATFORM_LINUX || defined PLATFORM_APPLE
return timegm(&t);
#else
return pContext->ThrowNativeError("Platform has no implemented UTC conversion for std::tm to std::time_t");
#endif
}

static cell_t GetPluginIterator(IPluginContext *pContext, const cell_t *params)
{
IPluginIterator *iter = scripts->GetPluginIterator();
Expand Down Expand Up @@ -1088,6 +1133,7 @@ REGISTER_NATIVES(coreNatives)
{"ThrowError", ThrowError},
{"GetTime", GetTime},
{"FormatTime", FormatTime},
{"ParseTime", ParseTime},
{"GetPluginIterator", GetPluginIterator},
{"MorePlugins", MorePlugins},
{"ReadPlugin", ReadPlugin},
Expand Down
2 changes: 1 addition & 1 deletion gamedata/sm-tf2.games.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
"StunPlayer"
{
"library" "server"
"windows" "\x55\x8B\xEC\x83\xEC\x20\x57\x8B\xF9\x8B\x87\x54\x04\x00\x00"
"windows" "\x55\x8B\xEC\x83\xEC\x20\x57\x8B\xF9\x8B\x87\xDC\x04\x00\x00"
"linux" "@_ZN15CTFPlayerShared10StunPlayerEffiP9CTFPlayer"
"mac" "@_ZN15CTFPlayerShared10StunPlayerEffiP9CTFPlayer"
}
Expand Down
2 changes: 1 addition & 1 deletion plugins/include/dhooks.inc
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,7 @@ native int DHookGamerules(Handle setup, bool post, DHookRemovalCB removalcb=INVA
*
* @param setup Setup handle to use to add the hook.
* @param post true to make the hook a post hook. (If you need to change the return value or need the return
* alue use a post hook! If you need to change params and return use a pre and post hook!)
* value use a post hook! If you need to change params and return use a pre and post hook!)
* @param addr This pointer address.
* @param removalcb Callback for when the hook is removed (Entity hooks are auto-removed on entity destroyed and
* will call this callback)
Expand Down
1 change: 0 additions & 1 deletion plugins/include/float.inc
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,6 @@ stock int RoundFloat(float value)
* User defined operators.
*/
#if !defined __sourcepawn2__
#pragma rational Float
// Internal aliases for backwards compatibility.
native float __FLOAT_MUL__(float a, float b) = FloatMul;
Expand Down
18 changes: 18 additions & 0 deletions plugins/include/sourcemod.inc
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,24 @@ native int GetTime(int bigStamp[2]={0,0});
*/
native void FormatTime(char[] buffer, int maxlength, const char[] format, int stamp=-1);

/**
* Parses a string representing a date and/or time into a unix timestamp.
* The timezone is always interpreted as UTC/GMT.
*
* See this URL for valid parameters:
* https://en.cppreference.com/w/cpp/io/manip/get_time
*
* Note that available parameters depends on support from your operating system.
* In particular, ones highlighted in yellow on that page are not currently
* available on Windows and should be avoided for portable plugins.
*
* @param dateTime Date and/or time string.
* @param format Formatting rules (passing NULL_STRING will use the rules defined in sm_datetime_format).
* @return 32bit timestamp (number of seconds since unix epoch).
* @error Invalid date/time string or time format.
*/
native int ParseTime(const char[] dateTime, const char[] format);

/**
* Loads a game config file.
*
Expand Down
50 changes: 49 additions & 1 deletion plugins/include/testing.inc
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ stock void SetTestContext(const char[] context)
strcopy(TestContext, sizeof(TestContext), context);
}

stock void AssertEq(const char[] text, int cell1, int cell2)
stock void AssertEq(const char[] text, any cell1, any cell2)
{
TestNumber++;
if (cell1 == cell2)
Expand All @@ -52,6 +52,39 @@ stock void AssertEq(const char[] text, int cell1, int cell2)
}
}

stock void AssertArrayEq(const char[] text, const any[] value, const any[] expected, int len)
{
TestNumber++;
for (int i = 0; i < len; ++i)
{
if (value[i] != expected[i])
{
PrintToServer("[%d] %s FAIL: %s should be %d at index %d, got %d", TestNumber, TestContext, text, expected[i], i, value[i]);
ThrowError("test %d (%s in %s) failed", TestNumber, text, TestContext);
break;
}
}
PrintToServer("[%d] %s: '%s' arrays are equal OK", TestNumber, TestContext, text);
}

stock void AssertArray2DEq(const char[] text, const any[][] value, const any[][] expected, int len, int innerlen)
{
TestNumber++;
for (int i=0; i < len; ++i)
{
for (int j=0; j < innerlen; ++j)
{
if (value[i][j] != expected[i][j])
{
PrintToServer("[%d] %s FAIL: %s should be %d at index [%d][%d], got %d", TestNumber, TestContext, text, expected[i][j], i, j, value[i][j]);
ThrowError("test %d (%s in %s) failed", TestNumber, text, TestContext);
break;
}
}
}
PrintToServer("[%d] %s: '%s' 2D arrays are equal OK", TestNumber, TestContext, text);
}

stock void AssertFalse(const char[] text, bool value)
{
TestNumber++;
Expand Down Expand Up @@ -93,3 +126,18 @@ stock void AssertStrEq(const char[] text, const char[] value, const char[] expec
ThrowError("test %d (%s in %s) failed", TestNumber, text, TestContext);
}
}

stock void AssertStrArrayEq(const char[] text, const char[][] value, const char[][] expected, int len)
{
TestNumber++;
for (int i = 0; i < len; ++i)
{
if (!StrEqual(value[i], expected[i]))
{
PrintToServer("[%d] %s FAIL: %s should be '%s' at index %d, got '%s'", TestNumber, TestContext, text, expected[i], i, value[i]);
ThrowError("test %d (%s in %s) failed", TestNumber, text, TestContext);
break;
}
}
PrintToServer("[%d] %s: '%s' arrays are equal OK", TestNumber, TestContext, text);
}
3 changes: 2 additions & 1 deletion plugins/include/tf2.inc
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,8 @@ enum TFCond
TFCond_LostFooting, //126: Less ground friction
TFCond_AirCurrent, //127: Reduced air control and friction
TFCond_HalloweenHellHeal, // 128: Used when a player gets teleported to hell
TFCond_PowerupModeDominant // 129: Reduces effects of certain powerups
TFCond_PowerupModeDominant, // 129: Reduces effects of certain powerups
TFCond_ImmuneToPushback // 130: Player is immune to pushback effects
};

const float TFCondDuration_Infinite = -1.0;
Expand Down
21 changes: 14 additions & 7 deletions plugins/nextmap.sp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ public void OnPluginStart()
RegAdminCmd("sm_maphistory", Command_MapHistory, ADMFLAG_CHANGEMAP, "Shows the most recent maps played");
RegConsoleCmd("listmaps", Command_List);

// Set to the current map so OnMapStart() will know what to do
HookEventEx("server_changelevel_failed", OnChangelevelFailed, EventHookMode_Pre);

// Set to the current map so OnConfigsExecuted() will know what to do
char currentMap[PLATFORM_MAX_PATH];
GetCurrentMap(currentMap, sizeof(currentMap));
SetNextMap(currentMap);
Expand All @@ -110,10 +112,18 @@ public void OnConfigsExecuted()
// not in mapcyclefile. So we keep it set to the last expected nextmap. - ferret
if (strcmp(lastMap, currentMap) == 0)
{
FindAndSetNextMap();
FindAndSetNextMap(currentMap);
}
}

public void OnChangelevelFailed(Event event, const char[] name, bool dontBroadcast)
{
char failedMap[PLATFORM_MAX_PATH];
event.GetString("levelname", failedMap, sizeof(failedMap));

FindAndSetNextMap(failedMap);
}

public Action Command_List(int client, int args)
{
PrintToConsole(client, "Map Cycle:");
Expand All @@ -129,7 +139,7 @@ public Action Command_List(int client, int args)
return Plugin_Handled;
}

void FindAndSetNextMap()
void FindAndSetNextMap(char[] currentMap)
{
if (ReadMapList(g_MapList,
g_MapListSerial,
Expand All @@ -149,14 +159,11 @@ void FindAndSetNextMap()

if (g_MapPos == -1)
{
char current[PLATFORM_MAX_PATH];
GetCurrentMap(current, sizeof(current));

for (int i = 0; i < mapCount; i++)
{
g_MapList.GetString(i, mapName, sizeof(mapName));
if (FindMap(mapName, mapName, sizeof(mapName)) != FindMap_NotFound &&
strcmp(current, mapName, false) == 0)
strcmp(currentMap, mapName, false) == 0)
{
g_MapPos = i;
break;
Expand Down
Loading

0 comments on commit 0638890

Please sign in to comment.