{
Summary:
Command interpreter and supporting code
## $Id: commands.pas,v 1.14 2004/04/10 22:24:03 druid Exp $
}
unit commands;
interface
uses
Classes,
{$IFDEF WIN32}
Windows,
{$ENDIF}
{$IFDEF LINUX}
Libc,
{$ENDIF}
SysUtils,
Math,
constants,
chars,
dtypes;
type
COMMAND_FUNC = procedure(ch : GCharacter; param : string);
GCommandFunc = class
private
_name : string;
_func : COMMAND_FUNC;
public
property name : string read _name write _name;
property func : COMMAND_FUNC read _func write _func;
end;
GCommand = class
private
_name : string;
_level : integer; { minimum level }
public
func_name : string;
ptr : COMMAND_FUNC;
allowed_states : set of STATE_IDLE .. STATE_SLEEPING; { allowed states }
addArg0 : boolean; { send arg[0] (the command itself) to func? }
property name : string read _name write _name;
property level : integer read _level write _level;
end;
var
funcList, commandList : GHashTable;
procedure interpret(ch : GCharacter; line : string);
procedure registerCommand(const name : string; func : COMMAND_FUNC);
procedure unregisterCommand(const name : string);
procedure initCommands();
procedure loadCommands();
procedure cleanupCommands();
implementation
uses
conns,
fsys,
strip,
util,
mudsystem,
console,
md5,
timers,
player;
procedure do_dummy(ch : GCharacter; param : string);
begin
ch.sendBuffer('This is a DUMMY command, and doesn''t perform any action.'#13#10);
ch.sendBuffer('Either this command has not been implemented yet,'#13#10);
ch.sendBuffer('or the server is reconfiguring itself with new code.'#13#10);
ch.sendBuffer('Please contact the administration if this persists for more than an hour.'#13#10);
end;
function findCommand(const s : string) : COMMAND_FUNC;
var
f : GCommandFunc;
begin
f := GCommandFunc(funcList.get(s));
if (f = nil) then
begin
writeConsole('Could not find function for command "' + s + '"');
Result := @do_dummy;
end
else
Result := f.func;
end;
// Load the commands
procedure loadCommands();
var
af : GFileReader;
param, s, g : string;
cmd : GCommand;
alias : GCommand;
begin
try
af := GFileReader.Create(SystemDir + 'commands.dat');
except
exit;
end;
repeat
repeat
s := af.readLine();
until (uppercase(s) = '#COMMAND') or (af.eof());
if (af.eof()) then
break;
alias := nil;
cmd := GCommand.Create();
cmd.allowed_states := [];
with cmd do
repeat
s := af.readLine();
g := uppercase(left(s,':'));
param := trim(right(s, ':'));
if (g = 'NAME') then
name := uppercase(param)
else
if (g = 'ALIAS') then
begin
// create an alias
alias := GCommand.Create();
alias.name := uppercase(param);
end
else
if (g = 'LEVEL') then
level := strtoint(param)
else
if (g = 'POSITION') then
begin
writeConsole('deprecated element position at line ' + IntToStr(af.line));
end
else
if (g = 'ALLOWED_STATES') then
begin
while (pos(',', param) > 0) do
begin
s := uppercase(left(param, ','));
if (s = 'IDLE') then
cmd.allowed_states := cmd.allowed_states + [STATE_IDLE]
else
if (s = 'FIGHTING') then
cmd.allowed_states := cmd.allowed_states + [STATE_FIGHTING]
else
if (s = 'RESTING') then
cmd.allowed_states := cmd.allowed_states + [STATE_RESTING]
else
if (s = 'MEDITATING') then
cmd.allowed_states := cmd.allowed_states + [STATE_MEDITATING]
else
if (s = 'SLEEPING') then
cmd.allowed_states := cmd.allowed_states + [STATE_SLEEPING];
param := right(param, ',');
end;
s := uppercase(left(param, ','));
if (s = 'IDLE') then
cmd.allowed_states := cmd.allowed_states + [STATE_IDLE]
else
if (s = 'FIGHTING') then
cmd.allowed_states := cmd.allowed_states + [STATE_FIGHTING]
else
if (s = 'RESTING') then
cmd.allowed_states := cmd.allowed_states + [STATE_RESTING]
else
if (s = 'MEDITATING') then
cmd.allowed_states := cmd.allowed_states + [STATE_MEDITATING]
else
if (s = 'SLEEPING') then
cmd.allowed_states := cmd.allowed_states + [STATE_SLEEPING];
end
else
if (g = 'FUNCTION') then
begin
func_name := right(s,' ');
ptr := findCommand(func_name);
end
else
if (g = 'ADDARG0') then
begin
addarg0 := (trim(uppercase(right(s,' '))) = 'TRUE');
end;
until (uppercase(s)='#END') or (af.eof());
if (cmd.allowed_states = []) then
cmd.allowed_states := [STATE_MEDITATING, STATE_IDLE, STATE_RESTING, STATE_FIGHTING];
if (assigned(cmd.ptr)) then
begin
commandList.put(cmd.name, cmd);
if (alias <> nil) then
begin
// update settings
alias.level := cmd.level;
alias.ptr := cmd.ptr;
alias.func_name := cmd.func_name;
alias.allowed_states := cmd.allowed_states;
alias.addarg0 := cmd.addarg0;
commandList.put(alias.name, alias);
end;
end
else
begin
cmd.Free();
if (alias <> nil) then
alias.Free();
end;
until (af.eof());
af.Free();
end;
// Strip '$' from commandline
procedure clean_cmdline(var line : string);
var
d : integer;
begin
d := pos('$', line);
while (d > 0) do
begin
delete(line, d, 1);
d := pos('$', line);
end;
end;
// Interpret the command
procedure interpret(ch : GCharacter; line : string);
var
a : longint;
gc : GCommand;
cmd : GCommand;
cmdline, param : string;
hash : cardinal;
iterator : GIterator;
timer : GTimer;
begin
if (not ch.IS_NPC) and (GPlayer(ch).switching <> nil) then
begin
interpret(GPlayer(ch).switching, line);
exit;
end;
{ Check if keyboard is locked - Nemesis }
if (not ch.IS_NPC) then
begin
if (GPlayer(ch).conn <> nil) and (ch.IS_KEYLOCKED) then
begin
if (length(line) = 0) then
begin
ch.sendBuffer('Enter your password to unlock keyboard.'#13#10);
exit;
end;
if (not MD5Match(GPlayer(ch).md5_password, MD5String(line))) then
begin
ch.sendBuffer('Wrong password!'#13#10);
exit;
end
else
begin
GPlayer(ch).afk := false;
GPlayer(ch).keylock := false;
act(AT_REPORT,'You are now back at your keyboard.',false,ch,nil,nil,to_char);
act(AT_REPORT,'$n has returned to $s keyboard.',false,ch,nil,nil,to_room);
exit;
end;
end;
{ AFK revised with keylock - Nemesis }
if (GPlayer(ch).conn <> nil) and (ch.IS_AFK) and (not ch.IS_KEYLOCKED) then
begin
GPlayer(ch).afk := false;
act(AT_REPORT,'You are now back at your keyboard.',false,ch,nil,nil,to_char);
act(AT_REPORT,'$n has returned to $s keyboard.',false,ch,nil,nil,to_room);
end;
end;
timer := hasTimer(ch, TIMER_ACTION);
if (timer <> nil) then
begin
act(AT_REPORT, 'You stop your ' + timer.name + '.', false, ch, nil, nil, TO_CHAR);
unregisterTimer(ch, TIMER_ACTION);
end;
if (length(line) = 0) then
begin
ch.sendBuffer(' ');
exit;
end;
// Char is being snooped
if (ch.snooped_by <> nil) then
GPlayer(ch.snooped_by).conn.send(line + #13#10);
clean_cmdline(line);
param := one_argument(line, cmdline);
cmdline := uppercase(cmdline);
cmd := nil;
hash := commandList.getHash(cmdline);
// try to find exact match first
iterator := commandList.buckets[hash].iterator();
while (iterator.hasNext()) do
begin
gc := GCommand(GHashValue(iterator.next()).value);
if (cmdline = gc.name) then
begin
cmd := gc;
break;
end;
end;
iterator.Free();
// if an exact match does not exist, try to find a substring
if (cmd = nil) then
begin
iterator := commandList.buckets[hash].iterator();
while (iterator.hasNext()) do
begin
gc := GCommand(GHashValue(iterator.next()).value);
if ((pos(cmdline, gc.name) = 1) and (length(cmdline) <= length(gc.name)) and (length(cmdline) > 1)) or
((copy(cmdline, 1, length(gc.name)) = gc.name) and (length(cmdline) = 1))
then
begin
cmd := gc;
break;
end;
end;
iterator.Free();
end;
if (cmd <> nil) then
begin
a := ch.getTrust();
if (a >= cmd.level) then
begin
if not (ch.state in cmd.allowed_states) then
case ch.state of
STATE_SLEEPING: ch.sendBuffer('You are off to dreamland.'#13#10);
STATE_MEDITATING: ch.sendBuffer('You must break out of your trance first.'#13#10);
STATE_RESTING: ch.sendBuffer('You are resting.'#13#10);
STATE_FIGHTING: ch.sendBuffer('You are fighting!'#13#10);
STATE_IDLE: ch.sendBuffer('You can not do that now.'#13#10);
else
writeConsole('Illegal state ' + IntToStr(ch.state) + '!');
end
else
begin
try
if (system_info.log_all) or (ch.logging) then
writeConsole(ch.name + ': ' + line);
if (cmd.level >= LEVEL_IMMORTAL) and (not IS_SET(GPlayer(ch).flags, PLR_CLOAK)) then
writeConsole(ch.name + ': ' + cmd.name + ' (' + inttostr(cmd.level) + ')');
if (cmd.addarg0) then
cmd.ptr(ch, cmdline + ' ' + param)
else
cmd.ptr(ch, param);
ch.last_cmd := @cmd.ptr;
except
end;
end;
end
else
cmd := nil;
end;
if (cmd = nil) and (not checkSocial(ch, cmdline, param)) then
begin
a := random(9);
if a<1 then
cmdline := 'Sorry, that command doesn''t exist in my vocabulaire!'
else
if a<2 then
cmdline := 'I don''t understand you.'
else
if a<3 then
cmdline := 'What are you saying?'
else
if a<4 then
cmdline := 'Learn some english!'
else
if a<5 then
cmdline := 'Hey, I don''t know that command. Try again.'
else
if a<6 then
cmdline := 'What??'
else
if a<7 then
cmdline := 'Huh?'
else
if a<8 then
cmdline := 'Yeah, right!'
else
if a<9 then
cmdline := 'What you say??';
act(AT_DGREEN, cmdline, false, ch, nil, nil, TO_CHAR);
end;
end;
// command stuff
procedure registerCommand(const name : string; func : COMMAND_FUNC);
var
g : GCommandFunc;
c : GCommand;
iterator : GIterator;
begin
g := GCommandFunc(funcList.get(name));
if (g <> nil) then
begin
bugreport('registerCommand', 'mudthread.pas', 'Command ' + name + ' registered twice.');
exit;
end;
g := GCommandFunc.Create();
g.name := name;
g.func := func;
funcList.put(name, g);
iterator := commandList.iterator();
while (iterator.hasNext()) do
begin
c := GCommand(iterator.next());
if (c.func_name = name) then
begin
// writeConsole('Found empty command with my name: ' + c.name);
c.ptr := func;
end;
end;
iterator.Free();
end;
procedure unregisterCommand(const name : string);
var
g : GCommandFunc;
c : GCommand;
iterator : GIterator;
begin
g := GCommandFunc(funcList.get(name));
if (g = nil) then
begin
bugreport('unregisterCommand', 'mudthread.pas', 'Command ' + name + ' not registered');
exit;
end
else
begin
iterator := commandList.iterator();
while (iterator.hasNext()) do
begin
c := GCommand(iterator.next());
if (@c.ptr = @g.func) then
begin
// writeConsole('Resetting command with my name: ' + c.name);
c.ptr := do_dummy;
end;
end;
funcList.remove(name);
g.Free();
end;
iterator.Free();
end;
procedure initCommands();
begin
funcList := GHashTable.Create(128);
commandList := GHashTable.Create(128);
commandList.setHashFunc(firstHash);
end;
procedure cleanupCommands();
begin
funcList.clear();
funcList.Free();
commandList.Free();
end;
end.