Vice City: Multiplayer

Server Development => Scripting and Server Management => Script Showroom => Topic started by: PSL on Apr 14, 2026, 11:07 AM

Title: Modular Support
Post by: PSL on Apr 14, 2026, 11:07 AM
This is a lightweight, modular management framework designed specifically for the Squirrel language. With zero dependencies and ready to use right out of the box, it comprehensively resolves the issues inherent in traditional single-script architectures—such as disorganization, difficulty in maintenance, lack of hot-updating capabilities, and event conflicts.

Through a unified module manager, the framework facilitates module loading, unloading, dependency management, lifecycle control, automatic global event dispatch, and secure timer isolation—thereby enabling your server scripts to truly achieve an engineered, modular, and extensible architecture.


Here is a simple example.

function onPlayerJoin(player) {
MessagePlayer("[#FFFF00]Hello " + player.Name, player);
}

function onScriptLoad() {
print("onScriptLoad: " + moduleName);
}

function onScriptUnload() {
print("onScriptUnload: " + moduleName);

a.Delete();
}

function hello(value) {
print("hello " + value);
}

a <- newTimer("hello", 1000, 0);

Place the script file into the "module" folder, then load the filename to add the script's functionality to the server.

ModuleManager.load("testModule");

Title: Re: Modular Support
Post by: Sebastian on Apr 18, 2026, 10:19 PM
This looks really good, and would have helped a lot the younger me.
Beginners should try to get used to this, as will make everything more clear in their scripts, and easier to debug. (especially when about leaks)

Great job!

PS:
There are some things you might want to re-check :D
1. The filePath in ModuleManager is looking for directory "modules", while in your .zip you have "module" directory
2. The newTimer function should use funcName instead of this.funcName
3. onPlayerActionChange parameters are wrong
4. In debug.nut you are missing .len() in cmd "modules" for(), and the player argument in MessagePlayer

PS2:
Ah, I thought it allows using same event in multiple modules

EDIT:
Seems like there is a bigger problem with your newTimer - passing vargv to NewTimer.
I remember somebody created one which accepts all types of data; you should check that out.
The very sample you provided isn't working due to that reason..

EDIT2: Ah, ofc was the lord @. S.L.C. Extended Timers (https://forum.vc-mp.org/?topic=1487.0)
Title: Re: Modular Support
Post by: PSL on Apr 24, 2026, 07:34 AM
Hey there! Thank you so so much for your detailed feedback and kind words, You're 100% right about all the issues you spotted—total my bad on the upload mix-ups and code oversights!  Also, thanks a lot for noticing the critical timer issue and mentioning S.L.C. Extended Timers. I will rebuild the timer system to support all argument types properly, so examples work correctly without errors.

Thanks again for helping me improve this framework, your feedback is really helpful. I'll upload the fixed and improved version soon.
Title: Re: Modular Support
Post by: PSL on May 06, 2026, 01:35 AM
This modular feature has been fully tested and runs flawlessly on a clean server. I have added an automated villa door—activated by pressing the 'M' key—as well as a test module for automatic health regeneration. Furthermore, the module can be modified using commands found within the debug module.

Within the module, you only need to focus on your new features. Please note that in `testModule`, timers must be created and destroyed using `newTimer` and `removeTimer`, respectively; furthermore, when using `vcmp` functions within `onScriptLoad`, you must employ weak references.
Title: Re: Modular Support
Post by: PSL on May 18, 2026, 08:41 AM
What is the next step?

function onScriptLoad() {
gate <- ::CreateObject(310, 1, -276.618, -495.992, 10.2778, 255);
gateStatus <- true;

key_M <- ::BindKey(true, 0x4D, 0, 0);
}

The `onScriptLoad` function within the module resides in a separate context table; therefore, to access methods located outside this table, you must use the double-colon weak reference syntax. This problem needs to be solved.

Additionally, the timer currently only supports creation and deletion; pause and resume methods still need to be added.

class WepPickup extends PickupClass {
wepID = null;
ammo = null;

constructor(model, pos, wepID, ammo) {
base.constructor(model, pos);
this.wepID = wepID;
this.ammo = ammo;
}

function onPickupPickedUp(player) {
player.GiveWeapon(wepID, ammo);
}
}

You can create custom classes that inherit from the `PickupClass` and override the `onPickupPickedUp` function to implement your own pickup logic. The `onPickupPickedUp` function executes automatically once a player picks up the item; simply creating a `WepPickup` instance within the `onScriptLoad` event is sufficient to implement a weapon pickup.

The Module Manager automatically dispatches the `onPickupPickedUp` event; by inheriting from `PickupClass`, the `onPickupPickedUp` method can be automatically executed within `WepClass`. That, in essence, is the underlying principle.