otland / forgottenserver

A free and open-source MMORPG server emulator written in C++
https://otland.net
GNU General Public License v2.0
1.59k stars 1.06k forks source link

Folder structuring. #1057

Open mindrage opened 9 years ago

mindrage commented 9 years ago

I find the data folder really useful when creating scripts, its as simple as selecting the event you want to make, then make the script for it. However. It also is something i think most people find annoying. Everything is clustered around all diffrent folders, you dont really have much options to organise. Yes, you could link the folders with the ".." paths but. nothing really anybody prefers to work with.

So.. i come with this suggestion instead. What if we made the data folders modular. Keep the same structure inside the data folder. But allow multiple data folders to be loaded in. and only load in those folders which exists inside a folder.

The folders will be linked via a xml file, which has these attributes:

this would basically make systems much more organised and work can be shared and edited separately. The current data folder could be called core, main, default or something.

So the general benefits: Grouping scripts, allowing people to work on a system and then only need to paste a folder to combine systems. Ability to disable systems Little to low coding, You practically only need to iterate the folders in the modules.xml file, and maybe make folders optional for everything except the main module. Backwards compability, Everything you written should work out of the box.

The drawbacks would be small. a bit longer loading time, since obviously more files to load. The modules obviously would contains alot of folders, ex Talkaction/scripts/ will still be there for every folder that uses a talkaction..

Since i've heard many people also wanted mods back into the code, this would actually be a simple but pretty flexible solution.

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

Nu77 commented 9 years ago

Why don't a function for load all scripts inside a folder and then load it in global.lua?

mindrage commented 9 years ago

Cause that is alot of work, and really, the functionailty would still be the same. No real need to make such ways. Also there is many ways to load in things. How exactly do you mean? One simple function to load a data folder?,. Or do you mean loading everything in by lua? It really doesnt fill out the details.

TheSumm commented 9 years ago

Not sure which is the right approach but I like simplifying the process of providing / adding new systems / events. Instead of describing what has to be added where you can just provide the folder.

This might however conflict with the idea/concept of the new events.

marksamman commented 9 years ago

I agree that we need more modularity for systems implemented in Lua. I'm just not sure about the idea of having multiple data folders, I'd rather have modules in a path like data/modules/. Here's my suggestion:

data/modules/shovel/module.xml:

<module name="Shovel" enabled="1">
    <action itemid="2554" script="scripts/use.lua" />
    <!--
    This should allow for any of our scripting interfaces, e.g. you could also add:
    <movevent event="StepIn" itemid="2554" script="scripts/stepin.lua" />
    -->
</module>

data/modules/shovel/scripts/use.lua:

function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if not target:isItem() or target:getId() ~= 481 then
        return false
    end

    target:transformTo(482)
    return true
end

I'm not entirely satisfied with that solution though, I'd rather find a way we could also hook into events (e.g. make use of Player:onLook() in a module). We would also have to worry about execution order/priority. Perhaps we could allow events to be registered in a modules XML file like this:

<event class="Player" method="onBrowseField" script="scripts/browsefield.lua" executionorder="-1" />

... where the execution order is 0 for the default events in data/events/, and -1 in this case would mean that this modules event will run before the default event (and anything above 0 would mean after), and if this modules event returns false then the default event won't run. Execution order would also be necessary for interfaces like actions (e.g. if two different modules register an action with the same item id, which one will run first?).

This issue is open for discussion, I'd love to hear other ideas or improvements on my suggestion.

Nu77 commented 9 years ago

Hooks to the new event interface will be awesome.

conde2 commented 9 years ago

Hooks for events can be done by Lua, something like Npcsystem do, would be easier and will work like a charm =)

mindrage commented 9 years ago

Well i did notice that when you use the form: @conde

function Creature:onSomething()
  return stuff
end

The function object could be obtained trough __newindex and hooked in that way. I thought first maybe you could have done something like

function Action{ itemid = 1432}:onUse()

end

However that would be a syntax error.

Hooking events trough lua would give much more control of things, though there would be alot of things what would get messy, I liked the Structure mark suggested as it made most sense for new scripters.

But somehow i also can imagine by hooking modules as actual lua modules.

Then the acutal choosing of where to hook would be simular to something like this.

local Demon = MonsterType("Demon")

function Demon:onCreatureAppear(creature)
    return true    
end

local Shovel = ItemType(xxxx)

function Shovel:onUse()

end

function Shovel:onStepIn(thing)

end

local player = Player("Name")

function player:onLogin()

end

The __newindex could simply link the reference of the function, and the actual classes for the Event Containers would only be used to verify. But then the problem would be here. How would you register these events on actual instances?

Anyone knows what im doing wrong with my markdown?..

Nu77 commented 9 years ago

Registers event would be awesome if we could do something like that:

local Shovel = {}

function Shovel.someOnUse()
        some stuff
end

function Shovel.someOnStepIn()
        some stuff
end

function Shovel.registerEvents()
        Shovel.someOnUse:registerOnUse(XXXX)
        Shovel.someOnStepIn:registerOnStepIn(XXXX)
end

Shovel.registerEvents()
mindrage commented 9 years ago

@nu77, Yeah This is all just speculation around. Hooking things trough lua would be a good way to handle the events. However it would break backwards compability quite alot. I think i heard @dalkon was working on a XML Parser in lua, which could basically make all the current systems be read and loaded in trough lua. That would work as a transition so the compability will atleast be there.

The point of having this was simply to make the handling of scripts more organized. No real need to actually bind things trough lua. But it surely gives more functionality by being able to to handle the creation and binding of events directly trough lua calls.

To be more to the point. The events must be able to link to multiple objects as easy as XML can link them. You still want an easy syntax to write with. Adding an extra function call to bind the event is not really anything newer people wants to do. The syntax: ˋˋˋ function Creature:onUse()

end ´´´ syntax was very good indeed and it can still be used with __newindex as a way to link things. However how the syntax should be to easily link it to an certain object. you could do something like this: ˋˋˋ Game.getItem(4321).onStepIn = function(self, fromPos, toPos) end ´´´ But to be honest.. That is quite ugly. I was planning on making some Data Collection library which could be used as a way to link multiple classes and preform operations on all. Where the getPlayers() would return a Group Object. Game.getPlayers():setLevel(8)

So.. i was thinking why not make something simular to bind Events? ˋˋˋ local Shovels = Game.getItems{ itemid = {1435, 2141}, actionid = 1003 } function Shovels:onUse() end ´´´ This basically means that the Server handles the "Group" class. and the group can be securely checked and all that.

Ezzz-dev commented 9 years ago

With this suggestion and all further comments, aren't we being literally redirected into Revscriptsys?

From Marksamman suggestion, it would look awful to have:

data/mobules/shovel/module.xml data/mobules/shovel/scripts/use.lua

It would mean we will have several folders and files just in the modules folder, which is really disorganized.

Can't we just have it like this:

<module name="Normal Shovel" enabled="1">
    <action itemid="2554" allowfaruse="no">
    <script type="onUse">
         --- lua code
    </script>
    <script type="onStepIn">
         --- lua code
    </script>
</module>
marksamman commented 9 years ago

There is nothing wrong with revscriptsys, I was actually planning on using revscriptsys before starting the development of 1.0, but because of @hjnilsson's inactivity and the fact that I had very little knowledge about the codebase I figured that it would be too much work to have everyone rewrite all their scripts to make the transition. Instead, we've been making incremental steps towards revscriptsys (unintentionally). I think we should definitely look at revscriptsys for inspiration of how to design a more modular script system, @hjnilsson has done a really good job with it and it's sad that it didn't get widely adopted.

As for your suggestion, Lua should not be written in XML files for many reasons, one of them being that debugging becomes harder. If we really want it to be a single file, then it should be entirely in Lua, example of how shovel.lua could look:

local function onUseShovel(player, item, fromPosition, target, toPosition, isHotkey)
    if not target:isItem() or target:getId() ~= 481 then
        return false
    end

    target:transformTo(482)
    return true
end
registerEvent({event = "onUse", itemid = 2554, callback = onUseShovel})
conde2 commented 9 years ago

^

That's what I meant when i said:

"Hooks for events can be done by Lua, something like Npcsystem do, would be easier and will work like a charm =)"

Nu77 commented 9 years ago

It's what i was trying to say, registering events should be possible in Lua without .xml file.

mindrage commented 9 years ago

I really do like the idea of binding all things directly in Lua, thats what i felt made Revscriptsys so attractive to me when it was under the works, however I can definately agree that when doing mods for example of the 0.3-0.4 branch of TFS, it made it really hard to acutally make stuff inside files, so you had to basically, make a separate file and link it, then go around and copy paste everything inside the file later, then test everything again just to make sure its working. The only benefit i see with using XML is the fact that its a data language, it doesnt execute any operations, which makes it a safety factor. it also looks a bit more organised, since well coders in the OT community arent all experienced and do not really think of how they want to organize their code, they simply want to working.

However i really do appreciate the benefits given from lua version, due to all of the possiblities you get with it. But still i prefer som kind of forced structuring so we actually have a standard for new people to try it out as well. this is still a community-focused software.

Nu77 commented 9 years ago

I think which .xml will continue and the change will be the new folder only... also think which .xml let all more disorganized, with hooks events is possible do a entire system with only one script, easy and fast to do.

Ezzz-dev commented 9 years ago

LUA script event registering is good, let's stop using ugly XML files :+1: besides that it will clean up the code it will make the interface more friendlier, and again it just shows that we're again moving forward to a more revscriptsys environment, too bad Revscriptsys didn't get the love it deserved.

conde2 commented 9 years ago

Maybe this implementation should work in a module folder for keeping Backwards compability.

Nu77 commented 9 years ago

I think we should look forward and use the total power of Lua Scripting, if you look around other lua engine implementations we see the use of hook binding, it's a powerfull feature. This could be done without affect backward scripts, then why not?

Zbizu commented 9 years ago

modules.lua could look like this:

registerAction(uniqueId, 30015, "--[[data/modules/]] quests/annihilator.lua")
registerMonster("Troll", "Trolls/troll.xml")

everything can be loaded from global.lua anyway so if you want to get rid of xml files, it may become main script loader, for example: global.lua

dofile('data/actions/actions.lua')

actions.lua would look like this:

registerAction(uniqueId, 30015, "--[[data/actions/scripts/]] quests/annihilator.lua")
slawkens commented 6 years ago

Basically what you mean is mods. They're already included in TFS 0.3/0.4. You can make multiple systems. The idea is simple: you have one XML file per system. In that file you can define as many actions/talkactions/items etc as you wish.

Example mod: https://github.com/slawkens/tfs-mods/blob/master/killed-monsters-counter.xml

I might make support for mods for TFS 1.0 when more people are interested.

WibbenZ commented 6 years ago

@slawkens Would be better to have it in Lua, trying to find errors etc with mods can sometimes be a pain. But I would love to have "mods" back - just keep it 100% Lua :p

mindrage commented 6 years ago

Yeah, the idea was just to be able to package things together for easier management and (maybe) distrobution. Having it in lua would be pretty great. but security is also a factor.

slawkens commented 6 years ago

The best solution would be probably adding simple lua functions for registering actions/items etc.

Like this:

function myFunction(cid, ...)
    print('Test')
end

Actions:registerActionId(300, myFunction)
Movements:registerUniqueId(100, myFunction)

local properties = {weight = 100, value = 1}
Items:register(2160, "gold coin", properties)

Talkactions:register("!mycommand", myFunction)

And then, make a directory (Like plugins/mods) where are .lua files will be automatically loaded from.

ranisalt commented 6 years ago

We are talking about an entire module restructuring, not just folders, and that probably will come after a Lua interface restructuring.

joseluis2g commented 6 years ago

Some days ago @TheSumm posted this on Discord https://github.com/TheSumm/forgottenserver/commits/action-script That's pretty clean imo

slawkens commented 6 years ago

A better system has been developed around 10 years ago on my old, slow PC, in Poland.

The system is called "mods" and is in TFS 0.3 series since ages.

Originally it was first introduced in 0.4.0_DEV, as pointed in CHANGELOG file of the revision 3777 of TFS 0.4 series, that is and was, by the way, the most successful series of TFS 0.3, and was originally developed by me, Elf and KaczooH.

I post a link with example mods developed for the system, if someone is interested: https://github.com/slawkens/tfs-mods

Yes, I was the original guy behind the idea. The whole concept is based on my vision, and the first implementation was done by me and Elf, around 10 years ago.

SO please, don't comment if you didn't developed something better. This is nothing personal to you @joseluis2g, thanks by the way, for refreshing this thread and posting something interesting.

yamaken93 commented 6 years ago

@slawkens https://github.com/HeavenIsLost/elysium/blob/master/data/actions/actions.lua Check this, i appreciate your feedback.

ranisalt commented 6 years ago

Hierarchical modules are nice, but not code inside XML. I would prefer something scripted in Lua instead.

slawkens commented 6 years ago

@ranisalt I also don't like the code inside of xml files, because you can't highlight it without plugins etc.. The examples I posted were just to demonstrate the power of the system. Normally it's possible to register them (any actions, events) in separate files. That's possible with the mods system. I just wanted to show that idea isn't new, and some work in this direction has been already done.