1// QFAdmin for QuakeForge 0.5.3+
2// Copyright (C) 2003 Harry Roberts
3
4// This program is free software; you can redistribute it and/or
5// modify it under the terms of the GNU General Public License
6// as published by the Free Software Foundation; either version 2
7// of the License, or (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12//
13// See the GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program; if not, write to:
17//
18//	Free Software Foundation, Inc.
19//	59 Temple Place - Suite 330
20//	Boston, MA  02111-1307, USA
21
22// Installing:
23//   Put qfadmin.gib in your server's gamedir (or id1 to be globally available)
24//   and put "exec qfadmin.gib" in your server.cfg.
25
26// Configuring:
27//   All qfamdin configuration is done via the "qfadmin" command.
28//
29//   qfadmin sub-commands:
30//      help		- Show list of commands & brief description
31//
32//	enable <name>	- Enable a !function
33//	disable <name>	- Disable a !function
34//
35//	set <name> <value> - Manually set a configuration value
36//	unset <name>	- Manually unset a configuration value
37//	value <name>	- Show a configuration value
38//
39//	maps [map1 ...]	- Show/set the list of maps available for voting
40//	maplist <file>	- Load the maplist from a file
41//	motd [yes/no]	- Show the motd upon connect?
42//	motdfile <file>	- File containing the motd
43//
44//   qfadmin configuration values:
45//      cheater.action	- What do do when somebody is !cheater'd out (kick or ban)
46//			  If an action is unknown (e.g. no kick/ban) it will default to kick
47//      cheater.bantime - How long they should be banned for (if action is ban)
48
49// Notes about the motd file:
50//   Special tokens can be embedded in the motd file which are replaced when sent to the client
51//	#players#	- Current number of players connected to the server
52//	#deathmatch#	- Current deathmatch mode
53
54// Sample Configuration:
55//   This example is taken from my server.cfg
56//
57//   exec qfadmin.gib
58//   qfadmin maps dm1 dm3 dm5 dm2
59//   qfadmin set cheater.action ban
60//   qfadmin set cheater.bantime 20
61//   qfadmin motd yes
62//   qfadmin motdfile welcome.txt
63
64// User commands:
65//   All these commands are used by saying !commandname as a regular user.
66//   Team talk is ignored.
67//
68//   !version		- Show the QFAdmin version running
69//   !cheater <id>	- Vote to kick/ban somebody
70//   !map <name>	- Vote to change map
71//   !maps		- Show a list of maps
72
73domain qfadmin
74
75global uservote cmds mapvote config
76//global cvsheader = "$Id$"
77global qfadminversion = "1.17"
78
79// QFAdmin console interface
80function qfadmin {
81	local cmd tmp
82
83	cmd = $args[1]
84
85	// Set a configuration value
86	if $(equal $cmd "set") {
87		tmp = $args[2]
88		config.$tmp = $args[3]
89
90	// Unset a configuration value
91	} else if $(equal $cmd "unset") {
92		tmp = $args[2]
93
94		ifnot $(length $config.tmp) {
95			echo "QFAdmin: value not set"
96			return
97		}
98
99		delete config.${tmp}
100
101	// Echo a configuration value
102	} else if $(equal $cmd "value") {
103		tmp = $args[2]
104
105		ifnot $(length ${config.$tmp}) {
106			echo "QFAdmin: value not set"
107			return
108		}
109
110		echo "QFAdmin: value of ", $tmp, " is \"", ${config.$tmp}, "\""
111
112	// Enable a command
113	} else if $(equal $cmd "enable") {
114		tmp = $args[2]
115
116		ifnot #cmd.$tmp {
117			echo "QFAdmin: command ", $tmp, " doesnt exist"
118			return
119		}
120
121		config.disable.${tmp} = "yes"
122
123	// Disable a command
124	} else if $(equal $cmd "disable") {
125		tmp = $args[2]
126
127		ifnot #cmd.$tmp {
128			echo "QFAdmin: command ", $tmp, " doesnt exist"
129			return
130		}
131
132		delete config.disable.$tmp
133
134	// View the list of voteable maps
135	} else if $(equal $cmd "maps") {
136		if (#args == 2) {
137			ifnot #config.maps {
138				echo "QFAdmin: no maps available"
139				return
140			}
141
142			echo "QFAdmin: maps available are" @config.maps
143			return
144		}
145
146		config.maps = @args[2:]
147
148	// Load a list of maps from a file
149	}  else if $(equal $cmd "maplist") {
150//		local line tmplist
151
152		if (#args == 2) {
153			echo "QFAdmin: must specify file to load maplist from"
154			return
155		}
156
157//		tmp = $args[2]
158
159//		for line in $(split $(file::read $tmp) "\n") {
160//			tmplist = $tmplist, " ", $line
161//		}
162
163//		config.maps = @tmplist
164		config.maps = $(split $(file::read $args[2]) "\n")
165
166	} else if $(equal $cmd "motd") {
167		if (#args == 2) {
168			ifnot $(length ${config.motd}) {
169				config.motd = "no"
170			}
171
172			echo  "QFAdmin: motd is set to \"", ${config.motd}, "\""
173			return
174		}
175
176		ifnot ($(equal $args[2] "yes") || $(equal $args[2] "no")) {
177			echo "QFAdmin: must be set either yes or no"
178			return
179		}
180
181		config.motd = $args[2]
182
183	} else if $(equal $cmd "motdfile") {
184		if (#args == 2) {
185			ifnot $(length ${config.motdfile}) {
186				echo "QFAdmin: no motd file set"
187				return
188			}
189
190			echo "QFAdmin: motd file is \"", ${config.motdfile}, "\""
191			return
192		}
193
194		ifnot $(count $(file::find $args[2])) {
195			echo "QFAdmin: cannot find motd file!"
196			return
197		}
198
199		config.motdfile = $args[2]
200
201	} else if $(equal $cmd "version") {
202		print "QFAdmin: ", $(qfadmin::getVersion), "\n"
203
204	// Print help information
205	} else if $(equal $cmd "help") {
206		echo "QFAdmin Help:"
207		echo "	set - set a config value"
208		echo "	unset - unset a config value"
209		echo "	value - display a config value"
210		echo "	enable - enable a command"
211		echo "	disable - disable a command"
212		echo "	maps - display/set the list of maps"
213		echo "	maplist - load the maplist from a file"
214		echo "	motd - show the motd upon connect"
215		echo "	motdfile - file to read motd from"
216		echo "	version - show qfadmin version"
217
218	} else {
219		echo "QFAdmin: unknown command \"", $cmd, "\""
220	}
221}
222
223// Initilize QFAdmin Settings
224function qfadmin::init {
225	function::export qfadmin
226	ifnot ${config.cheater.action} {
227		config.cheater.action = "ban"
228	}
229
230	ifnot ${config.cheater.bantime} {
231		config.cheater.bantime = 60
232	}
233}
234
235// Show version information
236function qfadmin::doVersion {
237	say $(qfadmin::getVersion)
238}
239
240// Return the current version
241function qfadmin::getVersion {
242//	local tmp tmp2
243
244//	tmp = $(split $cvsheader)
245
246//	if $(equal $tmp[6] "Exp") {
247//		tmp2 = "Beta"
248//	} else if $(equal $tmp[6] "Stab") {
249//		tmp2 = "Stable"
250//	} else if $(equal $tmp[6] "Rel") {
251//		tmp2 = "Release"
252//	}
253
254//	return "QFAdmin v", $tmp[2], " (", $tmp[3], " ", $tmp2, ") by Harry Roberts"
255	return "QFAdmin v", $qfadminversion, " by Harry Roberts"
256}
257
258// Clear uservore & mapvote vars
259function qfadmin::clearVars {
260	delete uservote
261	delete mapvote
262	global uservote
263	global mapvote
264}
265
266// Get the # of clients logged on
267function qfadmin::getClientNum {
268	return $(count $(client::getList))
269}
270
271// Vote to kick/ban somebody
272function qfadmin::doCheater {
273	local playerid isvalidid voteid lastvoteid
274	local name id
275
276	id = $args[1]
277	name = $args[2]
278	voteid = $args[3]
279
280	// They cant cheater their own id!!!
281	if ($id == $voteid) {
282		tell $id You cant cheater yourself
283		return
284	}
285
286	// Check the player exists on the server
287	for playerid in $(client::getList) {
288		if ($playerid == $voteid) {
289			isvalidid = 1
290		}
291	}
292	if (0$isvalidid != 1) {
293		return
294	}
295
296	lastvoteid = ${uservote.${voteid}.cheater}
297
298	// Cant vote for same ID twice in a row
299	if $(equal $lastvoteid $voteid) {
300		return
301	}
302
303	// Decrease the count for the last person they cheatered
304	if (0${lastvoteid} > 0) {
305		uservote.${lastvoteid}.cheated = (${uservote.${lastvoteid}.cheated} - 1)
306	}
307
308	uservote.${id}.cheater = $voteid
309	uservote.${voteid}.cheated = (0${uservote.${voteid}.cheated} + 1)
310
311	if (0${uservote.${voteid}.cheated} >= ($(qfadmin::getClientNum) / 2)) {
312		say $(client::getInfo $voteid "name"), " has been kicked for cheating"
313		qfadmin::clearVars
314
315		if $(equal ${config.cheater.action} "ban") {
316			ban $voteid ${config.cheater.bantime}
317		} else {
318			kick $voteid
319		}
320
321		return
322	}
323
324	say $name, " voted to ", ${config.cheater.action}, " ", $(client::getInfo $voteid "name"), " - with ", ${uservote.${voteid}.cheated}, " votes"
325}
326
327// Vote to change maps
328function qfadmin::doMap {
329	local map lastmap tmp isvalidmap
330	local name id
331
332	id = $args[1]
333	name = $args[2]
334	map = $args[3]
335
336	// Opps, no map
337	ifnot $(length $map) {
338		return
339	}
340
341	// Check the map exists
342	for tmp in @config.maps {
343		if $(equal ${tmp} ${map}) {
344			isvalidmap = 1
345		}
346	}
347
348	if (0${isvalidmap} != 1) {
349		return
350	}
351
352	lastmap = ${uservote.${id}.map}
353
354	// They cant vote for the same map twice in a row!
355	if $(equal $lastmap $map) {
356		return
357	}
358
359	// Decrease the vote count for the last map they voted for
360	if $(length $lastmap) {
361		mapvote.$lastmap = (${mapvote.$lastmap} - 1)
362	}
363
364	uservote.${id}.map = $map
365	mapvote.$map = (0${mapvote.$map} + 1)
366
367	// Have the majority of the users voted for this map? if so change it
368	if ( 0${mapvote.${map}} >= ( $(qfadmin::getClientNum) / 2 ) ) {
369		say "Switching to ", $map, " - with ", ${mapvote.$map}, " votes"
370		qfadmin::clearVars
371		map $map
372		return
373	}
374
375	say $name, " voted for ", $map, " - with ", ${mapvote.$map} , " votes"
376}
377
378function qfadmin::doMaps {
379	local name id tmp count
380
381	id = $args[1]
382	name = $args[2]
383
384	if (0$(count @config.maps) < 1) {
385		client::print $id "There are no maps available for voting\n"
386		return
387	}
388
389	client::print $id "There are ", $(count @config.maps), " maps available for voting\n"
390	for tmp in @config.maps {
391		client::print $id $tmp, " "
392
393		count = (0${count} + 1)
394		if ($count == 5) {
395			client::print $id "\n"
396			count = 0
397		}
398	}
399
400	client::print $id "\n"
401}
402
403// Perform whatever needs to be done when the client connects
404function qfadmin::connectEvent {
405	local name id
406
407	id = $args[1]
408	name = $args[2]
409
410	// Send them the MOTD
411	if $(equal ${config.motd} "yes") {
412		client::print $id "This server is using ", $(qfadmin::getVersion), "\n"
413
414		if $(length ${config.motdfile}) {
415			if $(count $(file::find ${config.motdfile})) {
416				local motdline
417
418				for motdline in $(split $(file::read ${config.motdfile}) "\n") {
419					motdline = $(regex::replace $motdline "#players#" -- $(qfadmin::getClientNum))
420					motdline = $(regex::replace $motdline "#deathmatch#" -- $deathmatch)
421					client::print $id $motdline, "\n"
422				}
423			}
424		}
425	}
426}
427
428// Clean up when a person disconnects
429function qfadmin::disconnectEvent {
430	local lastmap lastvoteid
431	local id name
432
433	id = $args[1]
434	name = $args[2]
435
436	lastmap = ${uservote.${id}.map}
437	lastvoteid = ${uservote.${voteid}.cheater}
438
439	if $(length $lastmap) {
440		mapvote.$lastmap = (${mapvote.$lastmap} - 1)
441	}
442
443	if (0${lastvoteid} > 0) {
444		uservote.${lastvoteid}.cheated = (${uservote.${lastvoteid}.cheated} - 1)
445	}
446
447	delete uservote.$id
448}
449
450// Add a user triggerable command
451function qfadmin::addCmd {
452	cmds.$args[1] = $args[2]
453}
454
455// Called whenever somebody talks
456function qfadmin::chatEvent {
457	local cmd cmdarg prefix from
458
459	// Ignore spectator/team messages
460	if $args[3] {
461		return
462	}
463
464	// Check the command starts with a !
465	prefix = $(slice $args[2] 0 1)
466	ifnot $(equal $prefix "!") {
467		return
468	}
469
470	// Process the command
471	from = $(client::getInfo $args[1] "name")
472	cmd = $(split $(slice $args[2] 1))
473
474	// Check if the command has been disabled
475	if $(equal ${config.disable.${cmd}} "yes") {
476		return
477	}
478
479	if #{cmds.$cmd} {
480		${cmds.$cmd} $args[1] $from @cmd[1:];
481	}
482}
483
484qfadmin::init
485
486event::register chat qfadmin::chatEvent
487event::register client.connect qfadmin::connectEvent
488
489// QFAdmin Commands
490qfadmin::addCmd "version" qfadmin::doVersion
491qfadmin::addCmd "cheater" qfadmin::doCheater
492qfadmin::addCmd "map" qfadmin::doMap
493qfadmin::addCmd "maps" qfadmin::doMaps
494