Browse Source

Initial plugin commit

nosoop 5 years ago
parent
commit
baa431c7bd
2 changed files with 162 additions and 1 deletions
  1. 22 1
      README.md
  2. 140 0
      scripting/csrd_bot_names.sp

+ 22 - 1
README.md

@@ -1,3 +1,24 @@
 # CSRD-BotNames
 
-Provides bot names to the Bot Manager from a local database.
+Provides bot names to the Bot Manager from a local database.
+
+## Database schema
+
+The local database must have the following schema at minimum; the plugin only selects from one
+column:
+
+```
+CREATE TABLE IF NOT EXISTS 'namelist' (
+	'name' TEXT NOT NULL
+);
+```
+
+## A "CSRD Internal" Plugin
+
+This is a plugin mainly intended for use in [Pikachu's Canadian Server of Romance and Drama].
+
+While I'm happy if you find some useful code for your own plugins, I can't provide any support
+for said code, nor will I offer any guarantees that the plugin will remain usable outside of its
+main use.
+
+[Pikachu's Canadian Server of Romance and Drama]: https://csrd.science/

+ 140 - 0
scripting/csrd_bot_names.sp

@@ -0,0 +1,140 @@
+/**
+ * Sourcemod 1.7 Plugin Template
+ */
+#pragma semicolon 1
+#include <sourcemod>
+
+#include <tf2>
+#include <botmanager>
+
+#pragma newdecls required
+
+#define PLUGIN_VERSION "0.1.2"
+public Plugin myinfo = {
+	name = "[CSRD] Bot Names",
+	author = "nosoop",
+	description = "Pulls bot names from a database.",
+	version = PLUGIN_VERSION,
+	url = "localhost"
+}
+
+Database g_NameDatabase;
+DBStatement g_hGetBotNames;
+
+/**
+ * We use a stack so players can't force reconnecting bots to get new names
+ * An ArrayList would work just as well, too.
+ */
+ArrayStack g_BotNames;
+
+char g_OriginalBotName[MAXPLAYERS+1][MAX_NAME_LENGTH];
+
+public void OnPluginStart() {
+	char error[256];
+	g_NameDatabase = SQLite_UseDatabase("bot-names", error, sizeof(error));
+	g_hGetBotNames = SQL_PrepareQuery(g_NameDatabase,
+			"SELECT name FROM namelist ORDER BY random() LIMIT ?;",
+			error, sizeof(error));
+	
+	if (!g_hGetBotNames) {
+		SetFailState("Failed to get bot names from database: %s", error);
+	}
+	
+	g_BotNames = new ArrayStack(ByteCountToCells(MAX_NAME_LENGTH + 1));
+}
+
+public void OnPluginEnd() {
+	delete g_hGetBotNames;
+	delete g_NameDatabase;
+}
+
+public void OnMapStart() {
+	// deal with late loads just to be safe
+	ArrayList usedBotNames = new ArrayList(ByteCountToCells(MAX_NAME_LENGTH + 1));
+	int nExistingBots;
+	for (int i = 1; i <= MaxClients; i++) {
+		if (IsClientInGame(i) && IsFakeClient(i)) {
+			char name[MAX_NAME_LENGTH];
+			GetClientName(i, name, sizeof(name));
+			
+			usedBotNames.PushString(name);
+			
+			nExistingBots++;
+		}
+	}
+	
+	// use database
+	g_hGetBotNames.BindInt(0, MaxClients + nExistingBots);
+	
+	SQL_Execute(g_hGetBotNames);
+	while (SQL_FetchRow(g_hGetBotNames)) {
+		char name[MAX_NAME_LENGTH];
+		SQL_FetchString(g_hGetBotNames, 0, name, sizeof(name));
+		
+		if (usedBotNames.FindString(name) == -1) {
+			g_BotNames.PushString(name);
+		}
+	}
+	
+	delete usedBotNames;
+}
+
+public void OnMapEnd() {
+	// empty the bot name stack
+	EmptyStack(g_BotNames);
+	
+	for (int i = 1; i <= MAXPLAYERS; i++) {
+		g_OriginalBotName[i] = "";
+	}
+}
+
+/**
+ * Sets the name of a freshly-added bot.
+ */
+public int Bot_OnBotAdd(TFClassType &class, TFTeam &team, int &difficulty,
+		char name[MAX_NAME_LENGTH]) {
+	// pop the name off the stack
+	if (strlen(name) == 0 && !g_BotNames.Empty) {
+		g_BotNames.PopString(name, MAX_NAME_LENGTH);
+	}
+}
+
+/**
+ * Store the bot's name at the time of connection.
+ * We'll assume it's a valid name from the pool.
+ */
+public void OnClientConnected(int client) {
+	if (IsFakeClient(client)) {
+		GetClientName(client, g_OriginalBotName[client], sizeof(g_OriginalBotName[]));
+	}
+}
+
+/**
+ * Returns the bot name to the name stack.
+ */
+public void OnClientDisconnect(int client) {
+	if (IsFakeClient(client)) {
+		char name[MAX_NAME_LENGTH];
+		
+		if (strlen(g_OriginalBotName[client]) > 0) {
+			name = g_OriginalBotName[client];
+			g_OriginalBotName[client] = "";
+			g_BotNames.PushString(name);
+		}
+		/**
+		 * else plugin got reloaded or a TFBot snuck in so we have no idea what's up
+		 * don't worry about them
+		 */
+	}
+}
+
+/**
+ * Empties an ArrayStack.
+ */
+stock int EmptyStack(ArrayStack stack) {
+	int nPopped;
+	while (PopStack(stack)) {
+		nPopped++;
+	}
+	return nPopped;
+}