PtokaX forum

PtokaX => Support => Topic started by: Stravides on 21 February, 2004, 19:40:44

Title: Large vs Many - Comments Please
Post by: Stravides on 21 February, 2004, 19:40:44
Just a little note here really, I have been tinkering now for about 3 months with Lua and hubs and I have written a number of piddley bots that do generic functions and Plop has helped me to create smeagol...

I have pretty much merged all of these into one big 1000 line momma of a bot. (well to me its big..)

What are the Benefits / drawbacks re CPU / Memory of creating one large bot vs say 5-6 smaller ones.

Only about 2 needed to be registered, so no real need to count the number of bots on the user screen as a DB or Ben :)

Hope to hear from you soon

Always Learning
Stravides
Title:
Post by: kepp on 21 February, 2004, 20:05:10
QuoteOriginally posted by Stravides

What are the Benefits / drawbacks re CPU / Memory of creating one large bot vs say 5-6 smaller ones.

really, that depends on how the code is built up and what's in it
Title:
Post by: VERMiN on 29 February, 2004, 17:10:17
Agree!
Title:
Post by: NotRabidWombat on 29 February, 2004, 17:41:50
Many. Cause no one knows how to use hash tables. HAHAHAHA!

-NotRabidWombat
Title:
Post by: Skrollster on 29 February, 2004, 17:54:44
Couldn't you teach us how to use hashtables??
Title:
Post by: [NL]Pur on 29 February, 2004, 23:32:31
your the teacher ;)
Title:
Post by: Stravides on 01 March, 2004, 00:09:33
so just lemme get this straight, I ask for a perfectly sensible answer and get a semi serious answer that answers nothing and 4 complete wastes of time (5 if you include this)

I am trying to learn and just required a generic answer to my question... if you cant be bothered to answer then dont.. jus dont waste my time...  

infact - sod it I cant even be bothered...
Title:
Post by: NotRabidWombat on 01 March, 2004, 01:34:34
*yeesh* so maybe it took me a little while to find an example.  :-P

Anywho, shoutcast script is a pretty good example. The problem with the HUGE scripts:

a) Have a lot of features people don't need and can not easily remove (so much code)
b) They use massive sequential if/elseif/else statements. This will seriously affect performance.

If you take care of these two things, a script that combines many smaller ones will run faster.

You can look at retro bot by the smelly tezlo :-)
Or you can look at my incomplete bot:
GIR.lua
GIR = {};

GIR.sBotName = "GIR";

GIR.tTimerEvents = { n = 0 }; -- regular table/array
-- inner table, [0] = function, [1] = Time, [2] = Increment
GIR.tCommands = { }; -- associative table, cmd -> inner table
-- inner table = [0] to [n] vals for iProfile; "Func"; "Desc"
GIR.tDCCommands = { }; -- associative table, DCcmd -> inner table
-- inner table = [0] to [n] function calls

-- USER tinsert

function Main()
assert( dofile("modules\\GIR-Main.lua"), "GIR-Main.lua missing!"); -- ALL global

-- maybe write the quick assert function with the load module function
-- remember concating happens before string is sent... so FORMAT
end

function DataArrival(curUser, sData)
-- maybe CheckData should be moved...
if( strsub(sData, 1, 1) == "<" or strsub(sData, 1, 3) == "$To") then
-- TODO: gag check here

-- cmds override checking!
local s, e, cmd, args = strfind(sData, "%b<>%s+(%S+)%s*([^|]*)%|$");
local tCmdsLocal = GIR.tCommands; -- create local reference

-- DO NOT FORGET ABOUT !me cmd. That must make a call to checkdata
-- along with away, message, etc

if(cmd and tCmdsLocal[cmd] and (tCmdsLocal[cmd].Permit[curUser.iProfile] or tCmdsLocal[cmd].Permit[curUser.sName])) then
if(strsub(sData, 1, 1) == "<") then
if(tCmdsLocal[cmd].Silent ~= 1) then SendToAll(curUser, sData); end
return tCmdsLocal[cmd]:Func(curUser, args);
else
local s, e, toUser = strfind(sData, "^%$To:%s+(%S+)");
-- toUser = GetItemByName(toUser);
-- what if this is a PM to the bot? will GetItemByName work?
if(toUser) then
if(tCmdsLocal[cmd].Silent ~= 1) then SendToNick(toUser, sData); end
return tCmdsLocal[cmd]:Func(curUser, args, toUser);
end
end
else
-- This is all chatting data
return GIR.CheckData(curUser, sData);
end
else
local s, e, DCcmd, args = strfind(sData, "^(%S+)%s*([^|]*)%|$");
local tDCCmdsLocal = GIR.tDCCommands;

if(DCcmd and tDCCmdsLocal[DCcmd]) then
-- TODO
-- foreachi(...);

return -- something!
end
end

return 0;
end

function NewUserConnected(curUser)
GIR.CheckUser(curUser);
end

function UserDiconnected(curUser)

end

function OpConnected(curUser)
-- GIR.curUser = curUser;
-- assert( dofile("Modules\\OpConnected.lua") );
end

function OpDiconnected(curUser)

end

function OnTimer()
-- DO NOT user foreachi... use for/while loop
-- if I use tinsert, n will be set on all events
for i = 1, getn(GIR.tTimerEvents) do

end
end

function InsertTimerEvent(TimeInSeconds, Function)
-- TODO
-- tinsert()
end
Oh ya! I just noticed. You also want to avoid having LOTS of global variables. Since all globals are contained in a hash table, having many will slow down performance  (that's one of the reason's Lua 5.0 has containers for builtin functions).
GIR-main.lua
do
-- load all cmd functions HERE
-- read ini file and point to functions in table
-- if functions are not pointed to, they go out of scope
-- garbage collector gets them

-- load the check functions TOO! if they don't get put into the check function list
-- in GIR class, they get removed from memory too!!!

-- Something like
-- get ini file
-- get cmd
dostring("GIR.tCommands["..cmd.."] = { Func = \""..sFunc.."\", Desc = \""..sDesc.."\"}"); -- wow, that's easy
-- throw in something for profile, from and to
end
basic functions.lua
-- GIR Commands --
-- These most all be global. They are pointed to by GIR.tCommands. Otherwise
-- they are lost to the garbage collector.

function Command_NickIPBan(Self, curUser, args)
local banUser, reason = GIR.Function_GetUserAndReason

if(banUser)
if( GIR.FunctionCheckRule(curUser, banUser, Self) ) then
if(reason ~= "") then
SendToAll(curUser.sName, banUser.sName.." is being IP/Nick banned. "..reason);
banUser:SendPM(curUser.sName, "You are IP/Nick banned. "..reason);
end

banUser:Ban();
banUser:NickBan();
else
-- Display can't do this message
end
else
-- print syntax from string table
end

return 1;
end

function Command_TempBan(Self, curUser, args)
local banUser, reason = GIR.Function_GetUserAndReason

if(banUser) then
if(reason ~= "") then
SendToAll(curUser.sName, banUser.sName.." is being temp banned for xx minutes. "..reason);
banUser:SendPM(curUser.sName, "You are being temp banned for xx minutes. "..reason);
end

banUser:TempBan();
else

end

return 1;
end

function Command_IPBan(Self, curUser, args)
return 0;

-- local s, e, IP, reason = strfind(args, "$(%S+)%s*(.*)");

-- if(IP) then

-- end
end

function Command_NickBan(Self, curUser, args)
local banUser, reason = GIR.Function_GetUserAndReason(args)

if(banUser) then
if(reason ~= "") then
SendToAll(curUser.sName, banUser.sName.." is being nick banned. "..reason);
banUser:SendPM(curUser.sName, "You are being nick banned. "..reason);
end

banUser:NickBan();
else

end

return 1;
end

function Command_TimeBan(Self, curUser, args)
local s, e, banUser, time, reason = strfind(args, "(%S+)%s+(%S+)%s*(.*)");

if(banUser) then
local mns, hrs, dys = strfind(strrev(time), "(%d+):?(%d*):?(%d*)"))

if(mns) then
dys = tonumber(dys) or 0;
hrs = tonumber(hrs) or 0;
mns = tonumber(mns) or 0;

if(reason ~= "") then
--
--
end

banUser:TimBan(mns + hrs * 60 + dys * 3600);
end
end
end

-- DOES NOT WORK ON TEMP BAN
function Command_UnBan(Self, curUser, args)
local s, e, toUnBan, reason = strfind(args, "(%S+)%s*(.*)");

if(toUnBan) then
if(reason ~= "") then
-- say something
--
end

UnBan(toUnBan);
end
end

function Command_RegisterUser(Self, curUser, args)
local s, e, sNick, sPass, iProfile = strfind(args, "(%S+)%s+(%S+)%s+(%d+)");

if(sNick) then
iProfile = tonumber(iProfile);
if(iProfile and GetProfileName(iProfile) and GIR.Function_CheckRule(curUser, iProfile, Self.Rule)) then

end
end
end

function Command_UnRegisterUser(Self, curUser, args)

end

function Command_ChangePassword(sNick, sPassword)

end

function Command_ChangeHubName(sName)

end

function Command_ChangeHubDescription(sDesc)

end

function Command_WhoIs(sNick)

end

function Command_PrivateMessageOperators(sNick, sMessage)

end

function Command_RequestVIP(sNick)

end

function Command_DisplayRules(sNick, -- PM or chat? )

end

function Command_DisplayNetwork(sNick, -- PM or chat? )

end

function Command_DisplayTextFile(sNick, -- PM or chat? )

end

-- GIR Functions --

-- Function_GetUserAndReason --
-- IN: String containing nickname, with optional reason
-- FORMAT: "nick reason"
-- OUT: If nick is a valid user, user object with reason ("" if no reason)
-- Else nil, nil
function GIR.Function_GetUserAndReason(args)
local s, e, banUser, reason = strfind(args, "$(%S+)%s*(.*)");

-- pulling out all of the stunts for this one
-- (if banUser then return GetItemByName(banUser) else nil end)

return (banUser and GetItemByName(banUser)), reason;
end

function GIR.Function_CheckRule(fromUser, toUser, cmdObj)
if(cmdObj.Rule == "<>") then return 1; end

local fromUserRank, toUserRank;

-- TODO: create MIN, MAX function and use that for rank... always assume min

if(type(fromUser) == "table") then
fromUserRank = cmbObj.Permit[fromUser.sName] or cmdObj.Premit[fromUser.iProfile];
else
fromUserRank = cmbObj.Permit[fromUser];
end

assert(fromUserRank, "fromUserRank == nil!!!");

if(type(toUser) == "table") then
toUserRank = cmbObj.Permit[toUser.sName] or cmdObj.Premit[toUser.iProfile] or -1;
else
toUserRank = cmbObj.Permit[toUser] or -1;
end

return dostring(fromUserRank..cmdObj.Rule..toUserRank);
end

-- strrev --
-- IN: string
-- OUT: reverse of string
-- TODO: add this to string library
function strrev(str)
if(strlen(str) < 2) then
return str;
else
str = gsub(str, "^(.)(.*)(.)$", function(a, b, c) return c..strrev(b)..a; end, 1);
return str;
end
end

function GIR.tCommands:Function_InsertFunction(func, cmd, desc, permit, rule, lang, silence)
if(func and func ~= "" and cmd and cmd ~= "" and desc and desc ~= "") then
-- check for defaults, then assign
dostring("Self["..cmd.."] = {)");
end
end
The concept behind GIR is a create a table of functions that GIR needs based on a config file. If I do this inside a do... end block, all functions that are not used will be removed by the garbage collector, saving memory and making everything more efficient. Sort of a runtime efficency check.

-NotRabidWombat
Title:
Post by: NightLitch on 01 March, 2004, 01:38:15
Am really impressed Wombat! Nice!!! Gonna take some time reading this and understand a thing or two.

This is good... thx...
Title:
Post by: NotRabidWombat on 01 March, 2004, 01:39:27
Here is a breif readme I was working on for the bot. Also incomplete.
GIR is designed to be the most flexible and sophisicated lua script
for PtokaX. GIR is also designed for both users who know LUA and new
users.

-- Mobile Commands --
This is one of the key features of GIR. Much like PtokaX, GIR is designed
to encourage multiple languages. In fact, a translator does not have to know
LUA to change the way GIR acts and responds. All commmands are created in
a specific format in GIR's ini file. Their format is:

[Cmd] = {
Func = [[string]],
Desc = [[string]],
Permit = {...},
Rule = [[string]],
Lang = number,
Silent = number
};

Cmd - The command that triggers the function.
Func - Any of the command functions described below
Desc - Description will be displayed in help. You may use normal cariage returns or \n with this.
Permit - List of users and profiles permitted to use the command
Rule - Rule that determines what users the command can be performed on.
(<, <=, >, >=, ==, ~=, <>) <> is no rule. All values not included in the list are assumed
to be less than!
Lang - The language of the command/description. 1 to n (defined number of languages)
Silence - Hide command from main chat. 0 - no, 1 - yes

Here is an example:

[!Exile] = {
Func = [[Command_IPNickBan]],
Desc = [[Ban the IP and Nick of user]],
Permit = { RabidWombat,0,1 },
Rule = [[<]],
Lang = 1,
Silence = 1
};

This creates a new command for chat and PM (to the bot). The command is accessed
by !Exile. In the !help command, !Exile is described as the description (only for
the selected profiles). Nicks and profiles are listed as their integer representation
with commas deliminating them. The rule part is a comparison rule for what profiles this
command can be performed on. The list of nicks and profiles is ranked by it's order,
with the first value being the greatest and the last being the least. With "<"
(less than) as a rule, RabidWombat may !Exile any profile with 0, 1 (except a nick
with RabidWombat) but these profiles can do nothing to RabidWombat. The last part
of the command is the language identified with the command. This will be described
more thoroughly later.
Obviously, some of these commands are not performed to anyone and therefore the rule
is irrelevant.
Parts of a mobile command may be ommitted, except for Cmd and Desc. If they are ommited
the command will use the default values assigned at the beggining of the ini file.

-- Multiple Mobile Commands --
In addition to being able to have mobile commands, you can also have multiple mobile
commands. It is also possible to create another command in an entirely different language.

-- Languages --
Blah blah blah... Indexs get language values [English, Sweedish, ...]
So do commands

-- Defaults for Commands --
TODO

-- Flexible Memory --

Unlike other scripts, GIR is designed to only load functions for commands you have
enabled in GIR's ini file. If you do create a function you don't want available, GIR
saves memory and processor time. So if you don't want the flood command, simply remove
it from the ini file and that function will not remain in memory when GIR restarts.

-- Overriding PtokaX commands --

GIRs commands are designed to override the standard PtokaX commands for enhanced feature,
such as advertisement checking in the !me (third person speak command). To override these
commands one must only match that cmd text to the text of the desired command to be
overwriten. Additionally, a special function exists for the soul purpose of removing these
commands entirely. By using [Command_DoNothing], a command does nothing but go through the
normal data checking proceedure. It is ALSO not included in any help commands.

-- Adding Other Scripts 1,2,3... --
1)
2)
3)
You're done.

-- Things You Can't Do --
1) You can not have more than one command string. Trying to create to seperate type of !IPBan
(maybe on for Ops and one for Admins) will end badly. I am thinking about altering
this.
2) Transfer cake to hub users. :-( I'm working on it. Honest.

-- Commands, Syntax, and Arguments --
TODO

[Command_UniversalHelp] -- help that supports args with parameters for language
   if user has set language, no parameters are necessary

[Command_Help] -- Language specific help

[Command_SetLanguage] -- sets the language for the current user, ironically language specific

[Command_DoNothing] -- this function is meant for overriding PtokaX commands. instead of doing
nothing, this performs a normal checkdata function on the info passed
I can only hope that this is as useless as possible. Still feeling pretty negative ;-)

-NotRabidWombat

P.S. - You smell.
Title:
Post by: NotRabidWombat on 01 March, 2004, 01:40:47
LOL. What the hell was I thinking with 1.. 2... 3.. heh

I smell too. But not as bad as you.

-NotRabidWombat
Title:
Post by: NightLitch on 01 March, 2004, 01:44:10
hehe.. so you think I smell.. thx alot... ;-D

Am not quite sure how I should take that short sentence...

I stink in programming or I just smell in my words ?? lol...

/NL
Title:
Post by: NotRabidWombat on 01 March, 2004, 01:47:50
Heh. Body odor :-P

I had forgotten about this too.

"[Command_SetLanguage] -- sets the language for the current user, ironically language specific"

-NotRabidWombat
Title:
Post by: NightLitch on 01 March, 2004, 01:52:10
LOL! Now first after looking up what odour means in swedish, I must say... I smell like hoooggaaa....  think I need to take a shower before bedtime...

Have a good one NotRabidWombat / NL
Title:
Post by: Stravides on 01 March, 2004, 08:55:05
Many thanks. I really appreciate your help..

to pick up on a couple of points..

you mentioned nested ifs...  are we referring to

if then
if then
if then
dothis
else
dothis
end
else
dothis
end
else
dothis
end

************  or ***********

if then
dothis
else
dothis
end
if then
dothis
else
dothis
end


or both ?  

Secondly I have  a good number of globals in an external config file, is this also bad ?  I use this for the rules etc, hubname blah blah blah :)

Thanks again, I Really appreciate your help here
Title:
Post by: [NL]Pur on 01 March, 2004, 12:29:20
2) Transfer cake to hub users. :-( I'm working on it. Honest.
 


:)

nice concept rabid
Title:
Post by: NotRabidWombat on 01 March, 2004, 13:33:39
Stravides:

The ifs I am talking about are:

if(cmd == "Exile") then
elseif(cmd == "Something else") then
elseif(cmd == "Another thing") then
another 60 elsesif's

This is bad.

Even worse.
if(strsub(cmd, 2, 5) == "Exile") then

Yes, having many globals (which usually happens when combine many small scripts) is bad. My solution for that is presented in the skeleton of GIR. Make a table for all globals in a specific script. This helps organize and performance.

Heh, cake.

-NotRabidWombat
Title:
Post by: Skrollster on 04 March, 2004, 17:19:13
OK, realy nice notrabitwomen...

this is a framework that i used then i was working on gs 2.0, hope any of you will find it intresting...

i don't have time to explane it, but i guess notrabbitwomen, tezlo, and probably some other guys will find it intressting reading...

It was too big to post here, but you can download it here (http://hem.bredband.net/light/Bot.lua)

Just never knew that it was called hashtables, been using it for a loong time now....

there might be some comments in there that will see strange to you though this is ripped from my bot..