Forum

> > CS2D > General > CS2D Modular Hooks and Commands System
ForenübersichtCS2D-ÜbersichtGeneral-ÜbersichtEinloggen, um zu antworten

Englisch CS2D Modular Hooks and Commands System

15 Antworten
Zum Anfang Vorherige 1 Nächste Zum Anfang

alt CS2D Modular Hooks and Commands System

Lee
Moderator Off Offline

Zitieren
One of the main problems with incorporating Lua into CS2D that I've thought up of is this. What if a person wants to run multiple mods on the same server? Well, the only way to do it would be to hardcode everything into a single mod, and that could potentially scare the server operator away, not to mention the hassles of having to make sure the variables don't override each other, which would indeed be a catastrophe. So I've created a system to run multiple mods at the same time called ModHCS. The beauty of this is that the modding process would only differ in a very minute scale. And given that everything is semantic, you won't ever have to worry about what does what and at where.

The attached is an untested version (or loosely tested by simulating its behaviors) and is obviously unfinished.

A test module template:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
testMod_module = {}


--************--
--*Initialize*--
--************--

function testMod_init(arg)
	--just print to the server output stream.
	pr(1, "asdf")
end
testMod_module.init = function(arg) testMod_init(arg) end

--******--
--*Acts*--
--******--

testMod_module.acts = {}

--Sample command - sampleAct - parameters {p, typ, cmd}

function testMod_sampleAct(p, x, cmd)
	if (cmd) then cmd = "sampleAct Command with the parameters: "..cmd else cmd = "sampleAct command with no parameters" end
	msg2(p, cmd)
end

--Add the sampleAct command to the acts list - Do not forget to use all lower for the name of the field
testMod_module.acts.sampleact = function(p, x, cmd) testMod_sampleAct(p, x, cmd) end

--Admin Acts
testMod_module.admacts = {}
--Admin Privilege
testMod_module.privilege = {}

--Sample command - sampleAdmAct - parameters {p, typ, cmd}

function testMod_sampleAdmAct(p, x, cmd)
	msg2(p, cmd)
end

--Add the sampleAdmAct command to the acts and privilege list - Do not forget to use all lower for the name of the field

testMod_module.admacts.sampleadmact = function(p, x, cmd) testMod_sampleAdmAct(p, x, cmd) end

testMod_module.privilege.sampleadmact= "a b c d e"

--*******--
--*Hooks*--
--*******--

function testMod_hooktype(arg)
	--does nothing here...
	print("calling hooktype with "..arg)
end

testMod_module.hooktype = function(arg) testMod_hooktype(arg) end



--********--
--*Return*--
--********--
return testMod_module

where any command to the server are called acts and any admin commands are called admacts. The hook functions are renamed from hook_hooktype to modName_hooktype, and then add the functions into the modName_module element and you're all set.

The ModHCS script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
description = {}

description.credits =
[[

Author: Lee Gao
License: Creative Common
Title: Modular Hook and commands system - ModHCS

]]

--************************--
--*Global Initializations*--
--************************--

users = {}
players = {}
modules = {}
admins = {}
cmd_priv = {}

--Temp msg2(p, t)
function msg2(p, t)
	print (p, t)
end

--Temp pr(p, t)
function d(t)
	print (t)
end

--*******************--
--*Utility Functions*--
--*******************--

function switch(t)
	t.act = function (self,x, p, com)
		local f=self[x] or self.default
		if f then
			if type(f)=="function" then
				f(p, x, com)
			else
				error("case "..tostring(x).." not a function")
			end
		end
	end
  	return t
end

function tableToString(t, n)
	local s = ""
	if (n == nil) then n = 1 end

	while (n <= #t) do
		s = s .. t[n] .. " "

		n = n + 1
	end
	return s
end

function admin_init()
	for line in io.lines("admins.cfg") do
		local tempUser = {}
		for word in string.gmatch(line, "%w+") do
			table.insert(tempUser, word)
		end
		users[tempUser[1]] = {password=tempUser[2], privilege = tempUser[3]}
	end
end

function mod_init(file)
	if (file == nil or type(file) ~= "string") then file = "mods.cfg" end
    for line in io.lines(file) do
		local f = loadfile(line)
		modules[line] = f()
		for k,v in pairs(modules[line].privilege) do
			tempPriv = {toString = v}
			for word in string.gmatch(v, "%w+") do
				tempPriv[word] = word
			end
			cmd_priv[k] = tempPriv
		end
	end

	local acts = {}
	acts.default = function(p, typ, cmd) msg2(p, "Error: Command Does Not Exist: "..typ) end
	local admacts = {}
	admacts.default = function(p, typ, cmd) msg2(p, "Error: Admin Command Does Not Exist: "..typ) end

	for mod_name, module in pairs(modules) do
		for act_name, act_fn in pairs(module.acts) do
			acts[act_name] = act_fn
		end

		for admact_name, admact_fn in pairs(module.admacts) do
			admacts[admact_name] = admact_fn
		end
	end

	action = switch(acts)
	admin_action = switch(admacts)
end

function parseCmd(t, priv, a)
	local cmd = {}
	local p = a[1]

	if (type(priv) ~= "boolean") then p = priv; priv = false end


	for word in string.gmatch(t, "%w+") do

		table.insert(cmd, word)
	end

	local typ = string.lower(cmd[1])
	table.remove(cmd, 1)

	if priv then
		if (typ == "login") then
			username = cmd[1]
			password = cmd[2]
			if (users[username]) then
				if (users[username].password == password) then
					admins[p] = users[username].privilege
					msg2(p, "User \""..username.."\" logged in successfully, your privilege mask is \""..admins[p].."\"")
				else
					msg2(p, "User Found but Incorrect Password")
				end
			else
				msg2(p, "No User with the Username: "..username)
			end
		elseif (cmd_priv[typ][admins[p]]) then
			local privilege = admins[p]

			command = tableToString(cmd)
			admin_action:act(typ, p, command)
		else
			msg2(p, "No admin privileges")
		end
	else
		command = tableToString(cmd)

		action:act(typ, p, command)
	end

end

--*******--
--*Hooks*--
--*******--
function hook_init()
	--Initialize the Mods system
	mod_init()
	--Initialize the User system
	admin_init()

	for modName, mod in pairs(modules) do
		if type(mod.init) == "function" then
			local f = mod.init()
		end
	end

end


function hook_say(p, t)

	if (string.lower(string.sub(t, 1, 1)) == "@") then
		t = string.sub(t, 1)
		parseCmd(t, true, {p})
	elseif (string.lower(string.sub(t, 1, 1)) == "/") then
		t = string.sub(t, 1)
		parseCmd(t, false, {p})
	end

	--hook_say shouldn't need a mod iteration.
end


--Substitute this for each of the hook type.
function hook_anything (p, arg1, arg2, argn)

	--hook_hooktype iteration

	for modName, mod in pairs(modules) do
		if type(mod.hooktype) == "function" then
			local f = mod.hooktype(p, arg1, arg2, argn)
		end
	end
end
hook_init()
hook_say(1, "/sampleAct 2")
hook_say(1, "@login adm")
hook_say(1, "@login admin")
hook_say(1, "@login admin pass")
hook_say(1, "@sampleAdmAct asdfasdf")

i=1
while (i<10) do
	hook_anything(i)

	i=i+1
end

@us Can you create a way to disable bubbling the behaviors of the hooks to the default when a return nil is added to the hook?

alt Re: CS2D Modular Hooks and Commands System

ohaz
User Off Offline

Zitieren
lol, much work without need!
There is a file called server.lua where you can write in all your lua mods and can run them all at the same time, even if they are not in the same file!

alt Re: CS2D Modular Hooks and Commands System

Tehmasta
User Off Offline

Zitieren
@TheKilledDeath, i don't think you understand what this is for. Lets say you want to incorporate two different types of mods, gungame and a weapons mod, normally, you would actually have to edit both of them in order to make it work.
Also, writing the same hook overwrites the other one, so you cant have 2 hook_says when your using two different mods. So if your running all your mods at the same time, they would get mixed up and you would end up with a messed up script.
Also, this restricts admin actions and adds an admin login system. It also makes added commands much easier and faster.
If your only running one mod, yes just putting everything in the server.lua would be the easiest, but most people won't just want to run one thing

alt Re: CS2D Modular Hooks and Commands System

Lee
Moderator Off Offline

Zitieren
TheKilledDeath hat geschrieben
lol, much work without need!
There is a file called server.lua where you can write in all your lua mods and can run them all at the same time, even if they are not in the same file!


like Tehmasta said, if you would need to run the Quake Sound mod plus the bet mod, the only viable alternative would be to rewrite the 2 mods into a single mod and refactor out a lot of stuff. This may be manageable for just 2 mods, I agree, and you may be able to sort out the conflicts in the commands system, but if you want to run like 4 mods at the same time, or if you want to edit the mods without having to change too much code, it just becomes way to tedious.

Currently, I've ported TMK's Bet mod, DC's Quake Mod, wrote a rank mod, and have incorporated all of them and gotten them to run without the slightest bit of problems at the same time. On the other hand, I've also made the structural arrangement of the system easier to add more commands and arrange the hooks without having to hard code anything. For example, if you want to add a command for teleport, all you would need is to do the following.

1. Create a new Lua file
2. Write modTeleport_module = {} at the top of the file
3. Write
1
2
3
4
5
6
7
8
9
10
11
12
13
modTeleport_module.admacts = {}
modTeleport_module.privilege= {}
function modTeleport_teleport(p, x, cmd)
	cmd = toTable(cmd)
	local id = cmd[1]
	local x = cmd[2]
	local y = cmd[3]
	setLocation(id, x, y)
end
modTeleport_module.admact.teleport = function(p, x, cmd) modTeleport_teleport(p, x, cmd) end

modTeleport_module.privilege.teleport = "a"
return modTeleport_module
4. Add the line "modTeleport.lua" into a central mods.cfg.


so now all admins with a privilege mask of "a" will be able to just type in "@teleport playerID x y" to teleport the player, without having to go through all of the trouble of rewriting a parse engine to parse the commands and hooking them. And plus, since people would want to run the teleport mod on top of other mod this becomes much easier for developers and users alike.

I will write a tutorial on developing for my system (which surprisingly uses considerably less steps than you would develop without using this system) but I will need DC to endorse this project first because otherwise there will be a significant number of developers who will be developing mods that are incompatible with other mods.

alt Re: CS2D Modular Hooks and Commands System

DC
Admin Off Offline

Zitieren
nice work. I also thought about the problem of running multiple mods at the same time.

one of my solutions would have been to change the hooking system a bit so that you can add as many functions to a hook as you like.
example:
1
2
3
4
5
6
7
8
function omg()
	msg("omg")
end
function lol()
	msg("lol")
end
addhook("say","omg")
addhook("say","lol")
(I saw something like that for garry's mod)

however there is still the problem of the return value. many hooks can have a return value which tells cs2d how to proceed/behave. but what if in this example omg returns 0 and lol returns 1? which value has to be used? it can only take one and not both.

a fusion of two mods which use the return value in the same hook(s) is probably not possible or would lead to unforeseen consequences...

alt Re: CS2D Modular Hooks and Commands System

Lee
Moderator Off Offline

Zitieren
Hmm, from what I've seen, the only cases where you would need a return value to cancel the bubbling effect of event delegation are when you would take a command and decide which function you would need to use, in the case you can switch the functions and have them on cases to return their respective values so if the function omg needs to return 1, then have a condition to see if the input triggers omg() and if it does, return the value of omg. (This would apply to hook say which would only parse one condition at a time).

However, in another case, we may just decide to return 1 if only one function needs to return 1.
Let's say we have a hook_kill, and if we return 1 on this function then the default behavior of kill is canceled. Then let's say that we hook 2 functions into it, one that checks if a person is killed by a certain weapon , and the other that auto sets the players health to 100 if he is killed (which would be impractical but for the sake of an example, let's assume this.) In this case the weapon condition would return 0 and the revive would return 1. In this case the need of the return depends on which act would supersede in priority over the other. So what we can do here is to have the function that needs the higher priority to return either a positive or negative 1, and have the return condition check if the abs(fn) > 0 and if it is positive we return 1 as a while and if it's negative we return 0 as a while.

This of course brings up the problem of having 2 equally prioritized returns, which means that this step can no kill the player but must kill the player to proceed. In which case the logic simply would not work even if we do find a way around this, so in this case we default to returning 0.

so for hook_say, we would do

function hook_say(p, t)
conditions, blah blah...
     return parseCmd(p, t)
end

for hook_kill:

function hook_kill(k, v, w)
     local ret = 0
     ret = ret+ function a() -- say this returns 1
     ret = ret+ function b() -- say this returns 0
     if (ret>0) then
          return 1
     end
     return 0
end

where if the numbers of functions needing 1 is greater than 0 or -1, then the function will cancel the default behavior, else it'll default to returning 0

alt Re: CS2D Modular Hooks and Commands System

DC
Admin Off Offline

Zitieren
hmm. I don't like the stuff with the negative numbers. I'll just take the latest return value of all added functions or 0/nil when no functions returns something.
and maybe I'll also add an optional priority parameter in the hook adding function (the return of the function with the highest priority counts)

which means that I'll now change the existing system and replace the command updatehooks with addhook("hookname","functionname",[maybe priority]) and removehook("hookname",functionname")

I think this is way better than handling this stuff in Lua. Moreover it forces scripters to make their mods "fusion-comptabile"

alt Re: CS2D Modular Hooks and Commands System

stealth
User Off Offline

Zitieren
another idea:

there are two kinds of such functions:
• those which are called when an event occurs which you can't cancel (like minute, startround). You can't return anything from those functions
• those which are called when an event occurs which you can cancel (like teamchange, spray, build, spawn, buy, ...). From those functions you can return 0 or 1. 0 doesn't cancel the event, 1 does.

Now cs2d should do this when an event occurs:
It should call all the functions which were added to the event one after the other, in the order in which they were added.
And if the event is an event which is cancelable, and a function returned 1, not only the event should be canceled, but also the execution of the following functions (those which were added after the one which returned 1)


Example:

you want to add two mods. one which plays those doublekill/monsterkill/... sounds (e.g. killsounds.lua), and one which disallows killing people which just spawned (e.g. spawnkill.lua).

spawnkill.lua would add a function to the kill-event, which returns 1 if the victim respawned in the last x seconds. now if this function returns 1, the player wont be killed. there could be mods which should not think the player got killed. like killsounds.lua: those sounds should not be played when the player wasnt killed.

so, in your server.lua file, you have to include the mods in the order in which their functions should be called:
1
2
dofile("spawnkill.lua")
dofile("killsounds.lua")
In this way, if spawnkill would cancel the kill, killsound would not notice the kill.
(There could also be mods which shall notice that someone tried to kill anyway. Those you would add above spawnkill.lua)

So, whenever a mod returns 1 and cancels an event, there can be other mods which shall not think the event really happened. Those you would add after the mod returning 1. Other example:

You have two mods. One which allows users to login in on the server by saying "/login user password" in chat, and one which creates a chatlog in a file which is then published on the server website.

You would add the login mod at first, because:
Whenever a user tries to login by saying a line which starts with "/login", this mod would return 1, so that his username and password are not shown in ingame chat.
His username and password would also not be shown to the chatlog-mod, because you added it after the login-mod, and the login-mod return 1.
You dont't want the login information to be published in chatlog.

Of course if you want to add a mod which shall see the login information anyway, you can add it before the login-mod.

Man this works for so many examples and is perfect;) And it is also very simple, you do only have to code the stuff into cs2d which i have written in italics above.

Of course you would also not need this stupid priority-parameter in addhook-function
1× editiert, zuletzt 09.03.09 15:26:56

alt Re: CS2D Modular Hooks and Commands System

DC
Admin Off Offline

Zitieren
well I think it's pretty random if using several scripts at the same time works without changes or not. no matter which method I use. no solution will work with all script combinations.

I already realized the changes as I wrote above with optional priorities. the return value of the function with the highest priority (or of the function which has been added last) counts.

alt Re: CS2D Modular Hooks and Commands System

Lee
Moderator Off Offline

Zitieren
@Stealth, do not use dofile since the function doesn't automatically write the global metatable, call loadfile(filename)() instead.

@DC
There's still a few problems with this. This is mostly the language design. First of all, whenever we do a load file the variables are written on a global scale. This means that if we have 2 different variables named players, then the most recently loaded mod's variable will overwrite those that are in the earlier system. If we load this without writing it to the global table, then the subsystems will not be able to access the CS2D functions.

On the priority solution, there's a problem of standardizing the priorities, for example, if one mod uses an arbitrary priority believing that the priority ranges from 1 to 10, and another uses a scale from 1 to 100, then obviously there's an arbitrary priority that doesn't really say anything about what the system really needs. I believe that the cleanest way is to have only 2 levels of priority, one is normal and the other supersedes the default priority. If there are contrasting priority then by logic the system would not work in either ways, so we default to normal. In this case we would not have an arbitrary priority scale for the user to decide on and we force them to conform to a standard so that it doesn't mess up the game logic.

@Stealth again
Zitat
You would add the login mod at first, because:
Whenever a user tries to login by saying a line which starts with "/login", this mod would return 1, so that his username and password are not shown in ingame chat.
His username and password would also not be shown to the chatlog-mod, because you added it after the login-mod, and the login-mod return 1.
You dont't want the login information to be published in chatlog.


Here's a simple rule of programming design to follow, do not ever let the user decide how the programming logic should go. In this case you're leaving it to the user to reason out that mod 1 will cancel a certain event in Mod2 so that it has to be loaded after the 1st mod. In this case, I'm willing to bet that none of the users will even give a seconds thought to which file should be loaded first because they would think that the order of loading is arbitrary. Even if you do issue out a disclaimer, that will only scare potential users away because you're forcing them to think about how the script works (which they absolutely do not care about).

Also, on a logic scale, the hook_say will still be called whenever you do something even if the default action is canceled. Since the only viable way of logging chat commands is through the hook_say hook, you'll either have to wait until the whole hook system is completed and then check if the previous returns are 0, which in this case means that you'll still have to hardcode in a central script to make sure that the hook delegated to chatlog will be given the parameter of the final return value, which itself will return that same value.

@DC again
Actually, this system would still need an intermediary because even if you do add the addhook command (which I do agree does make the system easier), you do still need a script that "compiles" or gathers all of the other subsystems together and then execute them all together, which would then expose them to a metatable of all events and hooks (much like my system actually) compiled and then iterate through the hooks each time it is called. The only upside is that the metatable isn't an actual physical table, but the concept would still be the same because in either cases the mods still need to be loaded to the same amount of memory and the same functions within the language must be called to access the elements of the arrays, whether it be a metatable or a table.

@Stealth, and DC
Zitat
spawnkill.lua would add a function to the kill-event, which returns 1 if the victim respawned in the last x seconds. now if this function returns 1, the player wont be killed. there could be mods which should not think the player got killed. like killsounds.lua: those sounds should not be played when the player wasnt killed.


This can be easily fixed by assigning a temporary local variable to keep track of all return values and if anyone of them is a 1, then scripts which depends on whether the player really is killed would simply check if the variable is 1 and execute if it is, those that do not need this check would simply not include that condition.

alt Re: CS2D Modular Hooks and Commands System

DC
Admin Off Offline

Zitieren
you can easily avoid function and variable overwriting by using a simple prefix for each global var and function in a mod like "function mymod_afunction()" or "mymod_player = ..." or by using the table stuff. pretty much the same what you did. I think I can't care about that within cs2d.

of course I have to agree that this is not the best solution and many scripters will probably not do it that way but it keeps scripts very simple and basic.

about the priorities:
the priorities are in fact pretty useless until you combine different scripts with the same hooks. they can THEN be used to define which of the scripts has to have a higher priority. of course it doesn't make much sense to define priorities for a single script.
it's impossible to run scripts with the same hooks + returns without any adjustments. the priority system makes these adjustments just as easy as possible.

alt Re: CS2D Modular Hooks and Commands System

Lee
Moderator Off Offline

Zitieren
DC hat geschrieben
you can easily avoid function and variable overwriting by using a simple prefix for each global var and function in a mod like "function mymod_afunction()" or "mymod_player = ..." or by using the table stuff. pretty much the same what you did. I think I can't care about that within cs2d.


Actually what I've done with a more recent version is that I've created a check globals functions to see if any of the variables used in the mod is already globally defined, and if it is, returns nil at the top of the file which means that the mod does not get loaded into the main chunk's module table.

Zitat
of course I have to agree that this is not the best solution and many scripters will probably not do it that way but it keeps scripts very simple and basic.

So can you give us an example of how to incorporate multiple mods into a single server? IE, with addhook, how do you run multiple mods at the same time? Wouldn't you still need a main script that gets all of the "subscripts", if you will, together and run them? The only difference here is that instead of adding the iterations into the main script via lua, the CS2D engine can handle that iteration, the execution speed however will remain the same since in either cases we will still be evaluating essentially the same functions.

Zitat
about the priorities:
the priorities are in fact pretty useless until you combine different scripts with the same hooks. they can THEN be used to define which of the scripts has to have a higher priority. of course it doesn't make much sense to define priorities for a single script.
it's impossible to run scripts with the same hooks + returns without any adjustments. the priority system makes these adjustments just as easy as possible.


Exactly, what I'm saying is that if there are different authors for the individual scripts, then how do you determine whose script is more prioritized? If author A wants his script to run at a priority of 10 and author B wants to run his script at a priority of 1000, would it really make sense to choose author B's over author A's script? Now let's say you changed the scale from 1-10, sure, there's a smaller limit of scale but who's to say that one script should be more important than another script if each scripter adds his priorities arbitrarily? Wouldn't it make more sense that if author A has a priority on returning 0 and author B has a priority on returning 1, that in such cases the 2 mods just aren't compatible with each other? That in this case, if both are evaluated simultaneously that it's obvious that for some reason, either:
1) The user decides against all common sense that 2 obviously incompatible mods such as auto-revive and a system which only lets you do something when you are dead just can not work together.
2) The author of the script decides to add a priority where they aren't needed.

In the first case, the only viable alternative is to have the user choose which mod he thinks should supersede the other, which imo isn't a very good system (it may be worthwhile to add it as an optional item however), in the 2nd, well, that should be included in the planning phase of the mod itself.

In either cases, you must understand that the user will not be able to get what he wants regardless of which system gets prioritized more so we shouldn't let him choose, just default to one or the other universally and hope that the developers would not be lazy and overlook adding a condition to check all possible previous return values.

Also, on the issue of my system, what I'm doing isn't exactly only multiloading mods, I've actually expanded it to a pretty large proportion so mod developers can access some very useful already developed functions and variables from low level data manipulation to higher level functions. For example, lua does not come with a native packer to pack and unpack data from it to its C-variant data form (the raw data used by CS2D) so I've ported 3rr0r's bb_udp script to lua and made my own packers to do the data conversion (IE: char to byte, 2chars to short, 4 chars to int, and pretty much what the Blitz WriteType and ReadType does [including converting the data into a virtual "stream" as seen in 3rr0r's original]), or the automatic command checking, the automatic global check, the parse, and etc, so I hope that you can rethink on still using my system.

alt Re: CS2D Modular Hooks and Commands System

DC
Admin Off Offline

Zitieren
yes you need a main script but this script does only one thing: including all the other scripts with dofile/loadfile or whatever.
It is much easier for scripters when they do not have to care about the iteration of hooked functions.

script developers should NOT set priorities at all. the priorities are optional and they default to 0. so I recommend to use addhook("bla","bla") and not addhook("bla","bla",mypriority)! as I said before the priority is just an aid for combining mods / sorting out problems which have been caused by combining them.

alt Re: CS2D Modular Hooks and Commands System

Lee
Moderator Off Offline

Zitieren
DC hat geschrieben
yes you need a main script but this script does only one thing: including all the other scripts with dofile/loadfile or whatever.
It is much easier for scripters when they do not have to care about the iteration of hooked functions.


Wait a second, the autohooking is done automatically so the developers would not need to worry about that in either case. And I made the system semantic while flexible so the script writer would only need to focus on the actual logic of the mod, not the internals.

And yes, I did implement the hooking using addhooks instead of iterating through a table and I've also made it so that even if a mod is not written using my system, the system can still load it.

What I'm trying to do with this system is to make it easier for people to write code. Lua is a very dynamic language, perhaps one of the most dynamic out there. It can be quite a struggle for new scripters to learn about Lua's scoping and how importing effects the main chunks (or even what chunks are and how they are executed). What I did is that I made it possible to just write the logic in something as close to english as possible, and at the same time made a bunch of helper functions so mods can be easily written and ran without any complications at all.

especially thanks to the help of being able to manipulate the global table, I can simplify the system into its bare minimums

for example

1
2
3
4
5
6
7
8
9
10
11
12
13
[b]My System[/b]
newMod("echo")
echo.local("prefix","Server echoed: ", "svar")

function act_echo(p, typ, cmd)
	msg2(p, echo.prefix..cmd)
end

function adm_echoprefix_a(p, typ, cmd)
	echo.setlocal("prefix", cmd, true)
end

return checkMod("echo")

is the equivalent to

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
[b]Conventional way of doing it[/b]
if _G.echo_say then return nil end
if _G.echo_prefix then return nil end

if not _G.addUsers then
	function addUsers()
		a few dozens of lines of code to create a users system
	end
	addhook("init","addUsers")
end

function echo_say(p, t)
	if string.lower(string.sub(t, 1, 5)) == "/echo" then
		if string.sub(t, 6, 6) ~= " " then
			return nil
		elseif #t <= 7 then
			return nil
		else
			cmd = string.sub(t, 7)
			msg2(p, echo_prefix..cmd)
		end
	elseif string.lower(string.sub(t, 1, 12)) == "@echo_prefix" then
		if string.sub(t, 13, 13) ~= " " then
			return nil
		elseif #t <= 14 then
			return nil
		else
			if users[p].privilege ~= "a" then
				msg2(p, "No admin privilege")
				return nil
			else
				cmd = string.sub(t, 14)
				msg2(p, "Server svar echo_prefix changed to "..cmd)
				echo_prefix = cmd
		end
	elseif not _G.users then
		.......
	end
	return 0
end
...
addhook("say", "echo_say")

This actually extends a few dozen more lines than is shown, but even the basic game logic is riddled with confusing conditions that are repetitive and can just be refactored.

Explanations. Here, when a user types in "/echo Hello World", the same player is greeted with the message "Server echoed: Hello World." Of course, if you're writting this the conventional way, you would not be able to change the leading character, whereas in mine you will have the flexibility to change what each leading character denotes. Then, when the user logs into his account and types in "@echo_prefix", or "@echoprefix", "@e_c_h_o_p_r_e_f_i_x" or any variant of thereof on my system, the game will automatically respond with a status message and change the variable.

You can decide which one is shorter, which one is easier to read and write and which one provides handy pre-written conditions, checks, and functions and etc.
Zum Anfang Vorherige 1 Nächste Zum Anfang
Einloggen, um zu antwortenGeneral-ÜbersichtCS2D-ÜbersichtForenübersicht