Liro - A modular gamemode base

https://app.alydus.net/static/alydus/liro/banner.png

What is Liro?*

Liro is a modular Garry’s Mod gamemode base. It has been designed from the ground up for code reusability and ease of use. With Liro there is two ways to design your gamemode, you may hardcode your gamemode into a single module, or separate your gamemode functions into different modules. Separation means you may benefit from reusing the modules in the future, you could also distribute the modules to others so they may benefit from your module(s) functionality.

Contributions
Liro is a base written from scratch and is fully open source, meaning you may contribute to the development of the base. At the moment, I am looking for contributors to help make some modules that will make some useful for inclusion in the releases.

Releases
Github Repo: https://github.com/Alydus/liro
Github Releases: https://github.com/Alydus/liro/releases/
Github Latest Release: https://github.com/Alydus/liro/releases/latest

Features
A optimized recursive module loader and clean output load logs
Global functions that can be reused from the Liro base files
Simple non-obstrusive console output
No messing about with include() and AddCSLuaFile()
Global (and per module) network strings defined in config, set before module initialization.
A easy to read and feature packed config
Configurable file prefixes (per module & global) for running files in different environments (e.g. sh_, sv_, cl_)
Ability to disable/enable modules in the base config. (gamemode/liro/config.lua)
All code is mostly all fully commented, meaning the code is easy to contribute to and modify to your own needs
Outdated version warning; automatically checks GitHub releases on post-initialization
Developer hooks (e.g. liro.successfullyLoadMODULENAME)

Note: A lot of Liro’s features are optionally meaning they can be disabled in the configuration file (liro/gamemode/liro/config.lua), features can also be configured here (obviously).

Developer Hooks


liro.attemptLoadModules - Called when liro attempts to load all of the modules
liro.attemptLoad(MODULENAME) - Called when the specified module attempts to load
liro.attemptCountModules - Called when liro attempts to count all the modules
liro.successfullyCountModules - Called when liro successfully loads all the modules
liro.successfullyLoaded(MODULENAME) - Called when the specified module attempts to load (returns moduleData as vararg)
liro.successfullyLoadedModules - Called when liro has successfully loaded all modules

Known Gamemodes using Liro

. Laser Tag (Released)
. Garry’s Murder Party! (Released)
. Lobby (Unreleased)

Note: Drop me a PM if you know (or own) a gamemode that is using Liro so I can add it to the thread!

Images
It’s hard to get many images of a gamemode base, but here’s the config.

config.lua (liro/liro/gamemode/liro/config.lua)

https://app.alydus.net/static/alydus/liro/config.png

registermodule.lua (liro/liro/gamemode/modules/examplemodule/registermodule.lua)

https://app.alydus.net/static/alydus/liro/registermodule.png

Example loading console output

https://app.alydus.net/static/alydus/liro/consoleoutput.png

I’ve been working on this base for a while now, I hope it comes in useful for any of your future projects. I’ve already been using it for a few little projects of mine and it’s really inspired me to make more gamemodes since the nitty gritty gamemode tasks aren’t needed.

Please don’t hesitate to leave some constructive advice in the thread comments, I’m looking to improve this as much as I can with the help of others.

Looks amazing dude!

Beautiful work, thank you for sharing!

Glad you like it.

I have had similar troubles with loading order. The way I solved it was by allowing a module to have dependencies. If a module wants to load, but it’s dependencies aren’t loaded yet, those get loaded first, then the module gets loaded. If those dependencies have dependence, you keep going. Be sure to avoid circular dependencies (I maintain a stack and check it every time a new dependency needs to be loaded). You may also want to maintain a dependency tree, so that you can reload modules + anything dependent on them on-the-fly (something I’m now suffering through, since I didn’t do it.)

I just looked at the github, 400 sloc? what even is this? :v

The reasoning for the high line amount is the clean syntax and the fact that I commented mostly everything for release, so others could contribute easier.

Thanks for your insight on my issue, dependencies sound like a good idea. I’ve been thinking recently about how modules could have metadata (supporting a easier module distribution), I had an idea for instead of Liro recursively including all of the module content, Liro would load a lua file within each module, with basic information like author, contact, description, and this lua file would call an event with the data as a parameter, Liro would pick this up and then load the rest of the module.

I’ve got quite a few ways of solving this, I’ll get to work as soon as I setup my development server.

[editline]15th May 2017[/editline]

I’ve just released a quick hotfix a few hours ago that fixes some fatal syntax errors, wasn’t aware of them on release as I didn’t get time to test the changes. Thanks to Vader for making me aware of these issues.

What about load priority for modules?

Interesting, I had just came up with this concept yesterday, I’m making really good progress on a overhaul of the module system right now.

See this issue on GitHub.

registermodule.lua (within each module)



-- Define module information
-- moduleFolderName must be set!
local moduleData = {moduleFolderName = "examplemodule", loadPriority = 5, author  = "Alydus", description = "A example module", website = "alydus.net", version = "0.1"}

-- Register the module
hook.Run("liro.registerModule", moduleData)


Massive overhaul coming soon, when I get time.

would this have the ability to make serious RP gamemodes? would love to see a new framework come into play

You could have module data specify a unique identifier for the modules, and then have a table of ‘dependancies’ which will look up whether the specified unique identifiers have been loaded or not.

If they haven’t, put that module in a table of modules pending dependancies and check for pending modules on the initialization of each proceeding module.

OR

Build a list of modules to be loaded and then sort them by dependancy. Ones with missing dependancies will just throw an error instead of loading.

edit:

You could also use https://github.com/bungle/lua-resty-tsort (A simple Lua implementation of topological ordering.

I wrote this in plain Lua 5.3.

output:



C:\Users\Dane\Desktop>lua53 init.lua
init    b
init    g
init    h
init    banana
module "test" is trying to load an unregistered module: "non-existant"
init    potato
init    a
init    c
init    d
init    f
init    e

C:\Users\Dane\Desktop>

codez:

[lua]
local exists = {}
local loaded = {}
local pending = {}
local broken = {}

– just a function to show when a module is initialized
local function init(name)
print(“init”, name)
end

local function addmodule(name, dependancies)
– register the module.
exists[name] = true

local good = true

-- insert into dependancy list.
if (dependancies ~= nil) then
	for k, v in ipairs(dependancies) do
		if (loaded[v] == nil) then
			pending[name] = pending[name] or {}
			table.insert(pending[name], v)
			good = false
		end
	end
end

-- initalize immediately if all dependancies were already loaded.
if (good) then
	loaded[name] = {name=name}
	init(name)
end

end

addmodule(“test”, {“non-existant”})
addmodule(“a”, {“potato”, “banana”})
addmodule(“b”)
addmodule(“c”, {“a”, “b”})
addmodule(“d”, {“c”})
addmodule(“e”, {“f”})
addmodule(“f”, {“d”, “g”, “h”})
addmodule(“g”)
addmodule(“h”)
addmodule(“potato”, {“banana”})
addmodule(“banana”)

local function loadmodules()
local continuing = true

while (continuing) do
	-- set this to false to assume this should be the last iteration.
	continuing = false

	for name, dependancies in pairs(pending) do
		for _, dependancy in ipairs(dependancies) do
			-- check if the dependancy exists
			if (exists[dependancy] == nil) then
				print("module \"" .. name .. "\" is trying to load an unregistered module: \"" .. dependancy .. "\"")
				broken[name] = true
				pending[name ] = nil
			end

			-- check if the dependancy has intersecting dependancies and remove the duplicate dependancies
			-- from this module.
			if (pending[dependancy] ~= nil) then
				for __, subdependancy in ipairs(pending[dependancy]) do
					if (subdependancy == dependancy) then
						dependancies[_] = nil
					end
				end
			end

			-- check if the dependancy has already been loaded.
			if (loaded[dependancy] ~= nil) then
				dependancies[_] = nil
			end
		end

		-- all dependancies have been loaded
		if (#dependancies == 0) then
			loaded[name] = {name=name}
			pending[name] = nil
			init(name)
		else -- we need to iterate again for further dependancies
			continuing = true
		end
	end
end

end

loadmodules()[/lua]

Of course, the different parts of the serious RP could be separated into modules to enable/disable at your request. Sounds good to me.

Thanks for the reply. I got another question though, is there a guide on how to make modules for Liro? I’m not that good at Lua, but I can learn if need be.

I’m working on a module system overhaul right now, but a quick rundown on the current system;

Create folder in /modules
Create lua files with the environment prefix defined (e.g. sv_, cl_, sh)

That’s about it really, liro is adding a lot more soon though.

What kind of people do you believe Liro is suited for? Beginner coders? Experienced?

Also, what do you want people to do with Liro that you believe they can’t do now? Or struggling doing now?

In my testing, I’ve found Liro to be extremely useful for quick prototyping of gamemodes and other code snippets. Since Liro isn’t a framework, I don’t particularly expect experienced coders to be using it as much as beginners.

Using Liro allows you to quickly disable/enable lua files within your gamemode, and totally removes the annoying aspect of manually including files.

I had an idea for a future marketplace of modules, so developers could distribute their modules. Allowing owners to quickly assemble a server experience of their choice.

Thanks for this! Well made from the looks of it.

I will be glad to use this and make some of my modules open source once you get your new module loader in.

This is super cool, great work!

Glad you like it.

I’m looking forward to your contribution of modules, it will really help the development of the base and help us think of some features that would make the base easier to develop upon.

And to everyone, I’ve been extremely busy working on lots of other sites at the moment so I’ve really took no time recently to work on the new module loader, I’ll take a look at it when I get time though.
I’m also considering participating in the latest competition and would probably like to use this for a base.

Fuck my life, I was just making one of these… ._.

Whats worse is that yours is better.
Does it have integrated Round System

No, but that is a planned module.