Browse Source

Initial commit

nosoop 7 years ago
parent
commit
4c1d4b5ce8
3 changed files with 277 additions and 0 deletions
  1. 3 0
      .gitmodules
  2. 273 0
      scripting/csrd_data_dump.sp
  3. 1 0
      scripting/include/stocksoup

+ 3 - 0
.gitmodules

@@ -0,0 +1,3 @@
+[submodule "scripting/include/stocksoup"]
+	path = scripting/include/stocksoup
+	url = https://github.com/nosoop/stocksoup

+ 273 - 0
scripting/csrd_data_dump.sp

@@ -0,0 +1,273 @@
+/**
+ * [CSRD] SRCDS Data Dumper
+ * 
+ * Uses SourceMod dumping commands to dump useful information for plugin developers.
+ */
+#pragma semicolon 1
+#include <sourcemod>
+
+#tryinclude <steamtools>
+
+#pragma newdecls required
+#include <stocksoup/log_server>
+#include <stocksoup/files>
+#include <stocksoup/version>
+
+#define PLUGIN_VERSION "1.0.0"
+public Plugin myinfo = {
+	name = "[CSRD] SRCDS Automatic Data Dumper",
+	author = "nosoop",
+	description = "Automatically dumps useful engine data for plugin stuff.",
+	
+	#if defined _steamtools_included
+	version = PLUGIN_VERSION,
+	#else
+	version = PLUGIN_VERSION ... "-no-steamtools",
+	#endif
+	
+	url = "https://git.csrd.science/"
+}
+
+#define REDUMP_MARKER_FILE "data/.force-data-dump"
+#define REDUMP_OUTPUT_DIRECTORY "data/datadump"
+
+// Use a map with as few entities as possible to ensure enough free edicts
+#define LOW_ENTITY_MAP "itemtest"
+
+enum DumpAction {
+	DumpAction_None = 0,		// no checks are performed
+	DumpAction_FileCheck,		// check if dump indicator is present
+	DumpAction_PendingDump		// pending dump on current map
+};
+
+DumpAction g_DumpAction;
+
+public APLRes AskPluginLoad2(Handle hPluginSelf, bool bLateLoaded, char[] error, int err_max) {
+	if (bLateLoaded) {
+		g_DumpAction = DumpAction_None;
+	} else {
+		g_DumpAction = DumpAction_FileCheck;
+	}
+	
+	return APLRes_Success;
+}
+
+public void OnPluginStart() {
+	RegAdminCmd("csrd_force_data_dump", ForceDataDump, ADMFLAG_ROOT);
+}
+
+public void OnMapStart() {
+	char path[PLATFORM_MAX_PATH];
+	BuildPath(Path_SM, path, sizeof(path), "%s", REDUMP_MARKER_FILE);
+	
+	switch (g_DumpAction) {
+		/** 
+		 * Stage 1:  Check if the marker file exists; change map if necessary.
+		 */
+		case DumpAction_FileCheck: {
+			if (FileExists(path, false)) {
+				g_DumpAction = DumpAction_PendingDump;
+				ForceChangeLevel(LOW_ENTITY_MAP, "Starting server data dump");
+			} else {
+				g_DumpAction = DumpAction_None;
+			}
+		}
+		
+		/**
+		 * Stage 2:  Validate map change and ensure dump is pending; start dumping if so.
+		 */
+		case DumpAction_PendingDump: {
+			char currentMap[PLATFORM_MAX_PATH];
+			GetCurrentMap(currentMap, sizeof(currentMap));
+			
+			if (StrEqual(LOW_ENTITY_MAP, currentMap) && FileExists(path, false)) {
+				PerformDataDump();
+				
+				DeleteFile(path);
+				
+				int version = GetNetworkPatchVersion();
+				LogMessage("Dumped server data for network patchversion %d", version);
+				
+				RequestFrame(ReloadServer);
+			} else {
+				LogError("Failed to change to map %s", LOW_ENTITY_MAP);
+			}
+		}
+	}
+}
+
+void PerformDataDump() {
+	char outputDirectory[PLATFORM_MAX_PATH];
+	BuildPath(Path_SM, outputDirectory, sizeof(outputDirectory), REDUMP_OUTPUT_DIRECTORY);
+	
+	CreateDirectories(outputDirectory, 0b111101000); // u+rwx,g+rx
+	
+	int version = GetNetworkPatchVersion();
+	
+	// does not spawn entities
+	ServerCommand("sm_dump_netprops %s/%d.netprops.txt", outputDirectory, version);
+	
+	DumpServerCommands("%s/%d.commands.txt", outputDirectory, version);
+	DumpServerConVars("%s/%d.convars.txt", outputDirectory, version);
+	
+	/**
+	 * spawns all entities, server will crash on map change -- make sure there are 1400-ish
+	 * free edicts
+	 */
+	ServerCommand("sm_dump_datamaps %s/%d.datamaps.txt", outputDirectory, version);
+	
+	// TODO replace this with just filtering the appropriate lines from datamaps?
+	// match expr /^[A-Za-z0-9_]+ - [A-Za-z0-9_]+$/
+	ServerCommand("sm_dump_classes %s/%d.classes.txt", outputDirectory, version);
+	
+	/**
+	 * TODO touch .server-version file in REDUMP_OUTPUT_DIRECTORY to notify incron of completed
+	 * dump
+	 */
+}
+
+#if defined _steamtools_included
+/**
+ * Steam master server has requested a restart; game version probably updated.
+ * Create the file indicating a data dump should be performed.
+ */
+public Action Steam_RestartRequested() {
+	PrepareRedump();
+	return Plugin_Continue;
+}
+#endif
+
+public void ReloadServer(any trash_professor_abacus) {
+	ServerCommand("quit");
+}
+
+public Action ForceDataDump(int client, int argc) {
+	PrepareRedump();
+	
+	ReplyToCommand(client, "[SM] Prepared dump process.  Reload plugin or restart server.");
+	
+	return Plugin_Continue;
+}
+
+/* Dump utilities */
+
+void DumpServerCommands(const char[] format, any ...) {
+	char outputPath[PLATFORM_MAX_PATH];
+	VFormat(outputPath, sizeof(outputPath), format, 2);
+	
+	File outputFile = OpenFile(outputPath, "w");
+	
+	char commandName[128], commandDescription[512];
+	bool bIsCommand;
+	int flags;
+	
+	outputFile.WriteLine("\"%s\",\"%s\",\"%s\"", "Names", "Flags", "Help Text");
+	
+	Handle hConCommand = FindFirstConCommand(commandName, sizeof(commandName), bIsCommand,
+			flags, commandDescription, sizeof(commandDescription));
+	
+	do {
+		if (bIsCommand) {
+			ReplaceString(commandDescription, sizeof(commandDescription), "\"", "'");
+			outputFile.WriteLine("\"%s\",\"%s\",\"%s\"", commandName,
+					GetCommandFlagString(flags), commandDescription);
+		}
+	} while ( FindNextConCommand(hConCommand, commandName, sizeof(commandName),
+			bIsCommand, flags, commandDescription, sizeof(commandDescription)) );
+	
+	delete outputFile;
+}
+
+void DumpServerConVars(const char[] format, any ...) {
+	char outputPath[PLATFORM_MAX_PATH];
+	VFormat(outputPath, sizeof(outputPath), format, 2);
+	
+	File outputFile = OpenFile(outputPath, "w");
+	
+	char commandName[128], commandDescription[512];
+	bool bIsCommand;
+	int flags;
+	
+	outputFile.WriteLine("\"%s\",\"%s\",\"%s\",\"%s\"", "Names", "Defaults", "Flags",
+			"Help Text");
+	
+	Handle hConCommand = FindFirstConCommand(commandName, sizeof(commandName), bIsCommand,
+			flags, commandDescription, sizeof(commandDescription));
+	
+	do {
+		if (!bIsCommand) {
+			char defaultValue[128];
+			FindConVar(commandName).GetDefault(defaultValue, sizeof(defaultValue));
+			
+			ReplaceString(commandDescription, sizeof(commandDescription), "\"", "'");
+			outputFile.WriteLine("\"%s\",\"%s\",\"%s\",\"%s\"", commandName,
+					defaultValue, GetCommandFlagString(flags), commandDescription);
+		}
+	} while ( FindNextConCommand(hConCommand, commandName, sizeof(commandName),
+			bIsCommand, flags, commandDescription, sizeof(commandDescription)) );
+	
+	delete outputFile;
+}
+
+char[] GetCommandFlagString(int flags) {
+	static char s_flagStrings[][] = {
+		"UNREGISTERED",
+		"DEVELOPMENTONLY",
+		"GAMEDLL",
+		"CLIENTDLL",
+		"HIDDEN",
+		"PROTECTED",
+		"SPONLY",
+		"ARCHIVE",
+		"NOTIFY",
+		"USERINFO",
+		"PRINTABLEONLY",
+		"UNLOGGED",
+		"NEVER_AS_STRING",
+		"REPLICATED",
+		"CHEAT",
+		"SS", // split screen
+		"DEMO",
+		"DONTRECORD",
+		"PLUGIN_OR_SS_ADDED", // split screen; same value as FCVAR_PLUGIN
+		"RELEASE",
+		"RELOAD_MATERIALS",
+		"RELOAD_TEXTURES",
+		"NOT_CONNECTED",
+		"MATERIAL_SYSTEM_THREAD",
+		"ARCHIVE_GAMECONSOLE",
+		"ACCESSIBLE_FROM_THREADS",
+		"SERVER_CAN_EXECUTE",
+		"SERVER_CANNOT_QUERY",
+		"CLIENTCMD_CAN_EXECUTE"
+	};
+	
+	char buffer[512];
+	
+	for (int i = 0; i < sizeof(s_flagStrings); i++) {
+		int flagbit = (1 << i);
+		
+		if (flags & flagbit) {
+			if (strlen(buffer)) {
+				StrCat(buffer, sizeof(buffer), " ");
+			}
+			StrCat(buffer, sizeof(buffer), s_flagStrings[i]);
+		}
+	}
+	
+	return buffer;
+}
+
+/* Dump marker functrions */
+
+void PrepareRedump() {
+	char path[PLATFORM_MAX_PATH];
+	BuildPath(Path_SM, path, sizeof(path), "%s", REDUMP_MARKER_FILE);
+	
+	TouchFile(path);
+}
+
+void TouchFile(const char[] file) {
+	File f = OpenFile(file, "w");
+	delete f;
+}

+ 1 - 0
scripting/include/stocksoup

@@ -0,0 +1 @@
+Subproject commit 908f29ba9c1bf6611510db8088199659bdfb8b67