Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - PSL

#1
Script Showroom / Modular Support
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.

  • Each module resides in a separate file with its own isolated scope, ensuring no mutual interference and resulting in a clean, easily maintainable code structure.
  • Supports single-module loading, batch loading, and dependency-aware loading; automatically detects whether a module has already been loaded to prevent redundant execution.
  • Features built-in `onScriptLoad` and `onScriptUnload` lifecycle hooks, which execute automatically whenever a module is loaded or unloaded.
  • Covers events across all scenarios—including server, player, vehicle, item, command, and chat interactions—with all modules automatically listening for these events, eliminating the need for manual hook binding.
  • Includes a dedicated `newTimer` function that binds timers directly to their respective modules; timers are automatically destroyed when a module is unloaded, effectively preventing memory leaks.
  • Enables cross-module function calls and variable access via `getModule()`, facilitating seamless functional interoperability between modules.
  • Supports the unloading of individual modules as well as a one-click option to clear all modules, allowing for hot-reloading without requiring a server restart.
  • Avoids polluting the global scope and leaves native logic unmodified; integration is straightforward and compatible with all standard Squirrel environments.

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");

#2
If class B extends Sqoin (i.e. class B inherits from class Sqoin), then inside class B you can directly use this.get(...) to get all injected modules

class B extends Sqoin {
constructor() {
local logger = get("Logger");
logger.log("B");
}
}

This usage, achieved by "inheriting the Sqoin class," is equivalent to giving Class B a direct access to the dependency repository. Class B automatically inherits all of Sqoin's dependency management capabilities, such as obtaining singletons and factory instances, and passing dynamic parameters (this.get("UserService", {id = 1})). It's like moving the dependency injection "toolbox" directly into Class B, making it more convenient to use.
#3
Currently implementing a flexible injection method of default parameters + dynamic parameters.

When registering a dependency, you can set a set of default configurations (for example, presetting the default output format for a logging tool); and when using the dependency, you can pass in new parameters at any time (for example, temporarily changing the output format to debug mode).
Dynamically passed parameters automatically override the default configuration, preserving the default settings while also adapting to temporary requirements in different scenarios. For example, you might use a regular database when registering a user service, but dynamically pass parameters to a test database during a subsequent call without having to modify the original registration rules. This provides flexibility and convenience.

module.factory("Logger", function(sqoin, args) {
return Logger(args.id);
}, {
id = 1
});

local logger1 = Sqoin.get("Logger");
local logger2 = Sqoin.get("Logger", {
id = 2
});

This code demonstrates the "default parameter + dynamic parameter" injection effect in the factory pattern:
1. When registering a Logger as a factory, default parameters are set via {id = 1}, serving as the base configuration for instance creation.
2. When logger1 is first retrieved without parameters, a Logger instance is created using the default parameters id = 1.
3. When logger2 is retrieved a second time with {id = 2}, the dynamic parameters override the default values, creating a new instance with id = 2.

Because of the factory pattern, each get call generates a new instance, and dynamic parameters take effect in real time, making it suitable for scenarios requiring differently configured instances.
#4
https://github.com/leitingzi/vcmp_server.git
My repository contains the core code for dependency injection and a test server that can be run.
#5
This Squirrel code implements a simple dependency injection (DI) framework with the following core features:

1. Provides a Module class for defining dependency bindings, supporting two binding types: single (singleton) and factory (factory). Register types and corresponding providers through the single() and factory() methods respectively.
2. Sqoin serves as the framework entry point and provides singleton context access (getInstance()), module loading (loadModules()), and instance acquisition (get()) functions through static methods.
3. SqoinContext is the core of the context, responsible for managing modules, storing singleton and factory registration information, and obtaining instances by type through the get() method (the singleton is created and cached by the provider when it is first obtained, and the factory creates a new instance each time it is called)

The overall dependency registration and resolution mechanism is implemented, which can be used to manage object dependencies in the server.

class Logger {
    function log(message) {
        print("Log: " + message);
    }
}

class UserRepository {
    logger = null;

    constructor(logger) {
        this.logger = logger;
    }

    function getUser(id) {
        this.logger.log("Getting user with id: " + id);
        return {
            id = id,
            name = "User " + id
        };
    }
}

class UserService {
    repo = null;
    logger = null;

    constructor(repo, logger) {
        this.repo = repo;
        this.logger = logger;
    }

    function getUserInfo(id) {
        this.logger.log("Fetching user info");
        return this.repo.getUser(id);
    }
}

This Squirrel code defines three business classes, forming a simple user information processing chain:
1. Logger class: Provides basic logging functionality, printing messages via the log method.
2. UserRepository class: Responsible for acquiring user data. Its constructor relies on Logger. It uses the getUser method to query user information based on ID and log it.
3. UserService class: Serves as the business service layer, relying on UserRepository and Logger. It encapsulates the user information acquisition process via the getUserInfo method, logging before calling repository layer methods.

The three collaborate through dependencies, embodying the layered design concept (log component, data access layer, business service layer), and can be combined with the dependency injection framework to manage dependencies.

local userModule = Module(function(module) {
        module.single("Logger", function(sqoin) {
            return Logger();
        });

        module.single("UserRepository", function(sqoin) {
            return UserRepository(sqoin.get("Logger"));
        });

        module.factory("UserService", function(sqoin) {
            return UserService(sqoin.get("UserRepository"), sqoin.get("Logger"));
        });
    });

This Squirrel code defines a dependency injection module userModule, which is used to configure the instance creation rules of each business class:
1. The configuration function is passed to the Module constructor, and the Logger and UserRepository are registered as singletons using the single method:
    1. The Logger instance is created directly using the no-argument constructor.
    2. The UserRepository depends on the Logger and constructs itself after obtaining the Logger instance through the dependency injection container.
2. The UserService is registered as a factory using the factory method: Each time a new UserService is constructed, the container retrieves the UserRepository and Logger instances.

This module declares dependencies and provides instance creation and dependency management rules for the previously defined Logger, UserRepository, and UserService, allowing them to be loaded and used by the injection framework.

local modules = [userModule];

Sqoin.loadModules(modules);

local userService1 = Sqoin.get("UserService");
local userService2 = Sqoin.get("UserService");

userService1.getUserInfo(123);
userService2.getUserInfo(456);

This Squirrel code example demonstrates the use of the dependency injection framework:
1. First, the previously defined userModule is added to the modules array. Sqoin.loadModules is used to load the module, completing dependency registration.
2. Sqoin.get("UserService") is called twice to obtain service instances (because UserService is registered as a factory, two different instances are generated).
3. The getUserInfo method is called on each UserService instance, passing in different IDs (123 and 456) to retrieve user information.

Running this code triggers logging and user data querying, demonstrating how the dependency injection framework manages object creation and dependency propagation. It also demonstrates the factory pattern's characteristic of generating a new instance each time a request is made.

#6
The code uses `org.json.JSONObject` to handle JSON data, but the JSON dependency is not included in the Maven `pom.xml` (you named it `Depidence.xml`, the standard name should be `pom.xml`). Direct compilation will result in a `ClassNotFoundException`
#7
This Squirrel code defines the `When` class and `when` function, enabling conditional logic functionality. The `When` class includes methods like `is` (equal to) and `isNot` (not equal to). If conditions are met, corresponding actions are executed and results are recorded, with only the first matching action triggered. Otherwise, all non-matching cases are handled, and the final result is returned.

class When {
    value = null;
    found = false;
    result = null;

    constructor(value) {
        this.value = value;
    }

    function is(expected, action) {
        if (!found && value == expected) {
            found = true;
            result = action();
        }
        return this;
    }

    function isNot(expected, action) {
        if (!found && value != expected) {
            found = true;
            result = action();
        }
        return this;
    }

    function isType(typeName, action) {
        if (!found && typeof value == typeName) {
            found = true;
            result = action();
        }
        return this;
    }

    function isNull(action) {
        if (!found && value == null) {
            found = true;
            result = action();
        }
        return this;
    }

    function inRange(min, max, action) {
        if (!found && value >= min && value <= max) {
            found = true;
            result = action();
        }
        return this;
    }

    function match(condition, action) {
        if (!found && condition(value)) {
            found = true;
            result = action();
        }
        return this;
    }

    function otherwise(action) {
        if (!found) {
            result = action();
        }
        return result;
    }
}

function when(value) {
    return When(value);
}

Here is an example
local x = 65;
local result = when(x)
    .isNull(@()"is Null")
    .isType("float", @()"Not Float")
    .is(100, @()"Max")
    .inRange(0, 35, @()"0~35")
    .inRange(35, 70, @()"35~70")
    .match(@(v) v * 2 > 150 && v < 80, @()"75~80")
    .otherwise(@()">80");

print(result); // out 0~35
#8
PassDataToServer

I can use the this function to send this table, but using this function in script uninstallation events is invalid。

What I can think of now is to use a client timer to implement it
#9
The player's client has a table that stores the state of the GUI. I hope that when the player quits, this table can be sent to the server and stored on the server, so that when the player logs in again, the table can be transmitted to restore the previous GUI state.
#10
Hi Habi,How to send the last data to the server using client-side scripts when a player quits, such as after entering /q?

I tried to perform a remote call to return data during the player exit event, but there was no response
#11
Community Plugins / Re: New Python Plugin
May 12, 2025, 05:08 AM
Well done! This plugin supports functional expansion, bringing a great deal of possibilities to VC-MP servers. I'm looking forward to seeing more developers creating interesting gameplay based on it!
#12
After my testing, this problem has been solved
#13
Hello, Hibi.
An error occurred with GetRemoteValue("print")("abc") on the client, typeof( GetRemoteValue("print")("abc") ) This returns type null.
When I use Exec(GetRemoteValue("print")("Have a nice day")) the client throws an error PeerExec: superblob expected but got null

    local userData = CallRemoteFunc( GetRemoteValue("print"),"123");
    Exec( userData );  //This is effective
#14
Hi, Habi.
I have been using the NPC plug-in you developed. The appearance of NPC has enriched the gameplay of the game. I would like to express my heartfelt thanks to you. After a period of use, I found that if you can add some specific functions to the plug-in, it may further improve its functionality and practicality. I would like to add some functions related to getting properties. I made a feature that fits my needs. Although I have already built it, I still want to integrate it into the plugin.
function OnPlayerStreamIn(playerid)
{
    print("npctest: OnPlayerStreamIn(playerid="+playerid+")\n");

//When you find a player, save player attributes first
getPlayerImmunity(playerid);
}

function OnPlayerStreamOut(playerid)
{
    print("npctest: OnPlayerStreamOut(playerid="+playerid+")\n");

//Here I have written a function that empties the player's attributes
}

function onGetImmunity(playerID, immunity){
print(GetPlayerName(playerID)+"'s Immunity: "+immunity+" \n");

//I store the data in a table and use the following function to get it when needed
}

function getPlayerImmunity(playerID){
//Get the player attributes I want from the table
}

function toGetPlayerImmunity(playerID){
RFC("getPlayerImmunity")(playerID, GetMyID());
}

//server
function getPlayerImmunity(playerID, npcID){
local plr = FindPlayer(playerID);
if(plr){
RFC(npcID, "onGetImmunity")(plr.ID, plr.Immunity);
}
}

Now you can use getPlayerImmunity to get the player's immunity in NPC scrpit. I've done a lot of things with this approach, such as player transparency, player cash, player deposits, whether a player can be attacked, player IP, UID, etc, Your varholder plugin has also helped me a lot.

I understand that adding these functions may increase your workload, but I believe that these new functions will not only meet my personal needs, but may also benefit more users, thus further increasing the value and popularity of the plug-in.
If you are willing to consider my request, I would be more than happy to provide you with more information about the specific details and usage scenarios of these functions to help you better understand my requirements. If there is anything I can do to help during the development process, such as testing new features, please feel free to ask and I will assist in any way I can.
Thank you again for developing such an excellent plugin and look forward to hearing from you.
#15
I can't wait to use it. Well done, Habi bro!