This commit is contained in:
Rushaway 2025-04-21 16:32:58 +02:00 committed by GitHub
commit 28f7176c2a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 855 additions and 495 deletions

6
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

89
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,89 @@
name: CI
on: [push, pull_request, workflow_dispatch]
jobs:
build:
name: "Build"
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04]
include:
- os: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Build sourcemod plugin
uses: maxime1907/action-sourceknight@v1
with:
cmd: build
- name: Create package
run: |
mkdir -p /tmp/package
cp -R .sourceknight/package/* /tmp/package
cp -R addons/sourcemod/configs /tmp/package/common/addons/sourcemod/
- name: Upload build archive for test runners
uses: actions/upload-artifact@v4
with:
name: ${{ runner.os }}
path: /tmp/package
tag:
name: Tag
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main'
- uses: dev-drprasad/delete-tag-and-release@v0.2.1
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main'
with:
delete_release: true
tag_name: latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: rickstaa/action-create-tag@v1
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main'
with:
tag: "latest"
github_token: ${{ secrets.GITHUB_TOKEN }}
release:
name: Release
if: startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main'
needs: [build, tag]
runs-on: ubuntu-latest
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
- name: Versioning
run: |
version="latest"
if [[ "${{ github.ref_type }}" == 'tag' ]]; then
version=`echo $GITHUB_REF | sed "s/refs\/tags\///"`;
fi
echo "RELEASE_VERSION=$version" >> $GITHUB_ENV
- name: Package
run: |
ls -Rall
if [ -d "./Linux/" ]; then
cd ./Linux/
tar -czf ../${{ github.event.repository.name }}-${{ env.RELEASE_VERSION }}.tar.gz -T <(\ls -1)
cd -
fi
- name: Release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: '*.tar.gz'
tag: ${{ env.RELEASE_VERSION }}
file_glob: true
overwrite: true

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
build/
release/
.DS_Store
.vscode
*.smx
plugins/
.sourceknight
.venv

View File

@ -0,0 +1,86 @@
;; Changes ammo to 8000 on every map-spawned weapon
;modify:
;{
; match:
; {
; "classname" "/weapon_.*/"
; }
; replace:
; {
; "ammo" "8000"
; }
;}
;; Remove game_end
;filter:
;{
; "classname" "game_end"
;}
;;Cool Music Entities
;add:
;{
; "origin" "0 0 0"
; "targetname" "GlobalAmbientGenericStripper"
; "spawnflags" "49"
; "radius" "1250"
; "pitchstart" "100"
; "pitch" "100"
; "message" "music/hl1_song10.mp3"
; "health" "10"
; "classname" "ambient_generic"
;}
;add:
;{
; "origin" "0 0 0"
; "targetname" "GlobalAmbientGenericStripper2"
; "spawnflags" "49"
; "radius" "1250"
; "pitchstart" "100"
; "pitch" "100"
; "message" "music/hl2_song23_suitsong3.mp3"
; "health" "10"
; "classname" "ambient_generic"
;}
;add:
;{
; "origin" "0 0 0"
; "targetname" "GlobalAmbientGenericStripper2"
; "spawnflags" "49"
; "radius" "1250"
; "pitchstart" "100"
; "pitch" "100"
; "message" "music/hl2_song23_suitsong3.mp3"
; "health" "10"
; "classname" "ambient_generic"
;}
;add:
;{
; "origin" "0 0 0"
; "targetname" "GlobalLogicTimerStripper"
; "RefireTime" "0.1"
; "spawnflags" "0"
; "StartDisabled" "1"
; "UseRandomTime" "0"
; "classname" "logic_timer"
;}
;add:
;{
; "origin" "0 0 0"
; "targetname" "GlobalGameTextStripper"
; "x" "-1"
; "y" ".20"
; "channel" "4"
; "message" "Default Message"
; "color" "0 255 255"
; "color2" "0 240 240"
; "fadein" ".1"
; "fadeout" ".1"
; "holdtime" "3"
; "spawnflags" "1"
; "classname" "game_text"
;}

View File

@ -0,0 +1,35 @@
;;EXAMPLE - remove all physics props
;remove:
;{
; "classname" "/prop_physics.*/"
;}
;;EXAMPLE - add the hostage to the map
;add:
;{
; "origin" "1376 3168 -112"
; "HostageType" "0"
; "classname" "hostage_entity"
}
;;EXAMPLE - replace all garbage cans with a hostage
;modify:
;{
; match:
; {
; "model" "models/props_junk/garbage_metalcan002a.mdl"
; "classname" "prop_physics_multiplayer"
; }
; replace:
; {
; "classname" "hostage_entity"
; }
; delete:
; {
; "model" "models/props_junk/garbage_metalcan002a.mdl"
; }
; insert:
; {
; "scale" "0.99"
; }
;}

View File

@ -3,13 +3,14 @@
#include <sourcemod>
#include <regex>
#include <Stripper>
public Plugin myinfo =
{
name = "Stripper:Source (SP edition)",
version = "1.3.1",
version = "1.3.3",
description = "Stripper:Source functionality in a Sourcemod plugin",
author = "tilgep, Stripper:Source by BAILOPAN",
author = "Original Author: BAILOPAN. Ported to SM by: tilgep. Edited by: Lerrdy, .Rushaway",
url = "https://forums.alliedmods.net/showthread.php?t=339448"
}
@ -71,20 +72,56 @@ enum struct Block
}
char file[PLATFORM_MAX_PATH];
char g_sLogPath[PLATFORM_MAX_PATH];
bool g_bConfigLoaded = false;
bool g_bConfigError = false;
Handle g_hFwd_OnErrorLogged = INVALID_HANDLE;
ConVar fileLowercase;
Block prop; // Global current stripper block
int section;
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
{
CreateNative("Stripper_LogError", Native_Log);
g_hFwd_OnErrorLogged = CreateGlobalForward("Stripper_OnErrorLogged", ET_Ignore, Param_String);
RegPluginLibrary("Stripper");
return APLRes_Success;
}
public void OnPluginStart()
{
prop.Init();
RegAdminCmd("stripper_dump", Command_Dump, ADMFLAG_ROOT, "Writes all of the map entity properties to a file in configs/stripper/dumps/");
RegAdminCmd("sm_stripper", Command_Stripper, ADMFLAG_GENERIC, "Prints out if the current map has a loaded stripper file");
fileLowercase = CreateConVar("stripper_file_lowercase", "0", "Whether to load map config filenames as lower case", _, true, 0.0, true, 1.0);
AutoExecConfig(true, "stripper");
}
public Action Command_Stripper(int client, int args)
{
bool bAccess = CheckCommandAccess(client, "sm_stripper", ADMFLAG_ROOT);
if (g_bConfigLoaded)
{
ReplyToCommand(client, "[Strippper] The current map has a loaded stripper config.");
if(bAccess) ReplyToCommand(client, "[Strippper] Actual cfg: %s", file);
}
else if (g_bConfigError)
{
ReplyToCommand(client, "[Strippper] The current map has a loaded stripper config but it contains error(s)");
if(bAccess) ReplyToCommand(client, "[Strippper] Check (%s)", file);
}
else
{
ReplyToCommand(client, "[Strippper] The current map did not load a stripper config.");
if(bAccess) ReplyToCommand(client, "[Strippper] No file found: (%s)", file);
}
return Plugin_Handled;
}
public Action Command_Dump(int client, int args)
{
char buf1[PLATFORM_MAX_PATH], buf2[PLATFORM_MAX_PATH], path[PLATFORM_MAX_PATH];
@ -92,9 +129,9 @@ public Action Command_Dump(int client, int args)
GetCurrentMap(buf1, PLATFORM_MAX_PATH);
BuildPath(Path_SM, buf2, PLATFORM_MAX_PATH, "configs/stripper/dumps");
BuildPath(Path_SM, buf2, PLATFORM_MAX_PATH, "logs/stripper/dumps");
if(!DirExists(buf2)) CreateDirectory(buf2, FPERM_O_READ|FPERM_O_EXEC|FPERM_G_READ|FPERM_G_EXEC|FPERM_U_READ|FPERM_U_WRITE|FPERM_U_EXEC);
if(!DirExists(buf2)) CreateDirectory(buf2, 0o666);
do
{
@ -107,7 +144,7 @@ public Action Command_Dump(int client, int args)
File fi = OpenFile(path, "w");
if(fi == null)
{
LogError("Failed to create dump file \"%s\"", path);
Stripper_LogError("Failed to create dump file \"%s\"", path);
return Plugin_Handled;
}
@ -138,31 +175,37 @@ public Action Command_Dump(int client, int args)
public void OnMapInit(const char[] mapName)
{
// Path used for logging.
BuildPath(Path_SM, g_sLogPath, sizeof(g_sLogPath), "logs/stripper/maps/%s.log", mapName);
g_bConfigLoaded = false;
g_bConfigError = false;
// Parse global filters
BuildPath(Path_SM, file, sizeof(file), "configs/stripper/global_filters.cfg");
ParseFile();
ParseFile(false);
// Now parse map config
strcopy(file, sizeof(file), mapName);
BuildPath(Path_SM, file, sizeof(file), "configs/stripper/maps/%s.cfg", mapName);
if(fileLowercase.BoolValue)
if(!ParseFile(true) && fileLowercase.BoolValue)
{
strcopy(file, sizeof(file), mapName);
for(int i = 0; file[i]; i++)
file[i] = CharToLower(file[i]);
BuildPath(Path_SM, file, sizeof(file), "configs/stripper/maps/%s.cfg", file);
ParseFile(true);
}
BuildPath(Path_SM, file, sizeof(file), "configs/stripper/maps/%s.cfg", file);
ParseFile();
}
/**
* Parses a stripper config file
*
* @param path Path to parse from
* @return True if successful, false otherwise
*/
public void ParseFile()
public bool ParseFile(bool mapconfig)
{
int line, col;
section = 0;
@ -175,19 +218,31 @@ public void ParseFile()
SMCError result = SMC_ParseFile(parser, file, line, col);
delete parser;
if (result == SMCError_Okay)
{
if (mapconfig)
g_bConfigLoaded = true;
return true;
}
if(result != SMCError_Okay && result != SMCError_StreamOpen)
{
if(result == SMCError_StreamOpen)
{
g_bConfigLoaded = false;
LogMessage("Failed to open stripper config \"%s\"", file);
}
else
{
char error[128];
g_bConfigError = true;
SMC_GetErrorString(result, error, sizeof(error));
LogError("%s on line %d, col %d of %s", error, line, col, file);
Stripper_LogError("%s on line %d, col %d of %s", error, line, col, file);
}
}
return false;
}
public SMCResult Config_NewSection(SMCParser smc, const char[] name, bool opt_quotes)
@ -197,7 +252,8 @@ public SMCResult Config_NewSection(SMCParser smc, const char[] name, bool opt_qu
{
if(prop.mode != Mode_None)
{
LogError("Found 'filter' block while inside another block at section %d in file '%s'", section, file);
g_bConfigError = true;
Stripper_LogError("Found 'filter' block while inside another block at section %d in file '%s'", section, file);
}
prop.Clear();
@ -207,7 +263,8 @@ public SMCResult Config_NewSection(SMCParser smc, const char[] name, bool opt_qu
{
if(prop.mode != Mode_None)
{
LogError("Found 'add' block while inside another block at section %d in file '%s'", section, file);
g_bConfigError = true;
Stripper_LogError("Found 'add' block while inside another block at section %d in file '%s'", section, file);
}
prop.Clear();
@ -217,7 +274,8 @@ public SMCResult Config_NewSection(SMCParser smc, const char[] name, bool opt_qu
{
if(prop.mode != Mode_None)
{
LogError("Found 'modify' block while inside another block at section %d in file '%s'", section, file);
g_bConfigError = true;
Stripper_LogError("Found 'modify' block while inside another block at section %d in file '%s'", section, file);
}
prop.Clear();
@ -231,12 +289,14 @@ public SMCResult Config_NewSection(SMCParser smc, const char[] name, bool opt_qu
else if(!strcmp(name, "insert:", false)) prop.submode = SubMode_Insert;
else
{
LogError("Found invalid section '%s' in modify block at section %d in file '%s'", name, section, file);
g_bConfigError = true;
Stripper_LogError("Found invalid section '%s' in modify block at section %d in file '%s'", name, section, file);
}
}
else
{
LogError("Found invalid section name '%s' at section %d in file '%s'", name, section, file);
g_bConfigError = true;
Stripper_LogError("Found invalid section name '%s' at section %d in file '%s'", name, section, file);
}
return SMCParse_Continue;
@ -256,7 +316,7 @@ public SMCResult Config_KeyValue(SMCParser smc, const char[] key, const char[] v
case Mode_Add:
{
// Adding an entity without a classname will crash the server (shortest classname is "gib")
if(StrEqual(key, "classname", false) && strlen(value) > 2) prop.hasClassname = true;
if(strcmp(key, "classname", false) == 0 && strlen(value) > 2) prop.hasClassname = true;
prop.insert.PushArray(kv);
}
@ -289,8 +349,13 @@ public SMCResult Config_EndSection(SMCParser smc)
{
if(prop.insert.Length > 0)
{
if(prop.hasClassname) RunAddFilter();
else LogError("Add block with no classname found at section %d in file '%s'", section, file);
if(prop.hasClassname)
RunAddFilter();
else
{
g_bConfigError = true;
Stripper_LogError("Add block with no classname found at section %d in file '%s'", section, file);
}
}
prop.mode = Mode_None;
@ -493,3 +558,18 @@ stock bool FormatRegex(char[] pattern, int len)
return false;
}
// native Stripper_LogError(const char[] format, any...);
public int Native_Log(Handle plugin, int numParams)
{
char sBuffer[2048];
FormatNativeString(0, 1, 2, sizeof(sBuffer), _, sBuffer);
LogToFileEx(g_sLogPath, "%s", sBuffer);
// Start forward call
Call_StartForward(g_hFwd_OnErrorLogged);
Call_PushString(sBuffer);
Call_Finish();
return 1;
}

View File

@ -0,0 +1,39 @@
#if defined _stripper_included
#endinput
#endif
#define _stripper_included
/*********************************************************
* Log a message into logs/stripper/maps/<mapname>.log
*
* @param format Message to log
* @noreturn
*********************************************************/
native void Stripper_LogError(const char[] format, any ...);
/**
* Called right after stripper loged a error.
*
* @param sBuffer Buffer to store the log message in.
* @param maxlen Size of the log buffer.
* @noreturn
*/
forward void Stripper_OnErrorLogged(char[] sBuffer, int maxlen);
public SharedPlugin __pl_stripper =
{
name = "Stripper",
file = "Stripper.smx",
#if defined REQUIRE_PLUGIN
required = 1
#else
required = 0
#endif
};
#if !defined REQUIRE_PLUGIN
public void __pl_stripper_SetNTVOptional()
{
MarkNativeAsOptional("Stripper_Log");
}
#endif

15
sourceknight.yaml Normal file
View File

@ -0,0 +1,15 @@
project:
sourceknight: 0.2
name: Stripper
dependencies:
- name: sourcemod
type: tar
version: 1.11.0-git6934
location: https://sm.alliedmods.net/smdrop/1.11/sourcemod-1.11.0-git6934-linux.tar.gz
unpack:
- source: /addons
dest: /addons
root: /
output: /addons/sourcemod/plugins
targets:
- Stripper