general cleanup of code and config files

- fixed indentation to 4 spaces
- added missing semicolons
- some other changes here and there that either make the code look consistent between examples or just make it more concise
This commit is contained in:
TheSparta 2024-05-20 17:05:33 +01:00
parent 8743824046
commit de74980eb4
57 changed files with 366 additions and 347 deletions

View File

@ -1,9 +1,10 @@
import { inject, injectable } from "tsyringe";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { Processing } from "./Processing";
@injectable()
export class MyMod
export class MyMod
{
// Since this is a singleton this class will only have 1 object/bean
private calls = 0;
@ -11,11 +12,11 @@ export class MyMod
// All these types are automatically wired when the container resolves the bean creation
constructor(
@inject("Processing") private processing: Processing,
@inject("WinstonLogger") private logger: ILogger
@inject("WinstonLogger") private logger: ILogger,
)
{}
public runModLogic(): void
public runModLogic(): void
{
this.processing.doProcess();
this.logger.info(`This is a singleton bean, so everytime its called the same entity will be returned. This is the call number: ${this.calls}`);

View File

@ -1,19 +1,20 @@
import { inject, injectable } from "tsyringe";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
@injectable()
export class Processing
export class Processing
{
// Since this is a transient type, this class will have many of this type
// Anything left in variables will always be discarded
private calls = 0;
constructor(
@inject("WinstonLogger") private logger: ILogger
@inject("WinstonLogger") private logger: ILogger,
)
{}
public doProcess(): void
public doProcess(): void
{
this.logger.info(`Process was triggered, since this is a singleton the calls value is always 0: ${this.calls}`);
}

View File

@ -1,4 +1,5 @@
import { DependencyContainer, Lifecycle } from "tsyringe";
import { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod";
import { IPostAkiLoadMod } from "@spt-aki/models/external/IPostAkiLoadMod";
import { MyMod } from "./MyMod";
@ -12,16 +13,16 @@ class Mod implements IPreAkiLoadMod, IPostAkiLoadMod
// This class is registered as a singleton. This means ONE and only ONE bean
// of this class will ever exist.
container.register<MyMod>("MyMod", MyMod, {lifecycle: Lifecycle.Singleton});
// This class is being registered as default or transient. This means that
// every time a class requests a bean of this type a new one will be created
container.register<Processing>("Processing", Processing);
}
public postAkiLoad(container: DependencyContainer): void
public postAkiLoad(container: DependencyContainer): void
{
// We will run this in a quick 5 loop to show how singletons and transients work
for (let i = 0; i < 5; i++)
for (let i = 0; i < 5; i++)
{
// every resolution will return the same MyMod bean
container.resolve<MyMod>("MyMod").runModLogic();

View File

@ -1,8 +1,8 @@
{
"manifest": [
{
"key": "assets/content/weapons/usable_items/item_bottle/textures/client_assets.bundle",
"dependencyKeys": []
}
]
"manifest": [
{
"key": "assets/content/weapons/usable_items/item_bottle/textures/client_assets.bundle",
"dependencyKeys": []
}
]
}

View File

@ -1,16 +1,17 @@
import { DependencyContainer } from "tsyringe";
import { IPostAkiLoadMod } from "@spt-aki/models/external/IPostAkiLoadMod";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
class Mod implements IPostAkiLoadMod
{
public postAkiLoad(container: DependencyContainer): void
public postAkiLoad(container: DependencyContainer): void
{
// get the logger from the server container
// get the logger from the server container
const logger = container.resolve<ILogger>("WinstonLogger");
logger.info("Loading: Bundle Loading Sample");
}
}
}
export const mod = new Mod();

View File

@ -1,3 +1,5 @@
import { inject, injectable } from "tsyringe";
import { LauncherCallbacks } from "@spt-aki/callbacks/LauncherCallbacks";
import { LauncherController } from "@spt-aki/controllers/LauncherController";
import { IEmptyRequestData } from "@spt-aki/models/eft/common/IEmptyRequestData";
@ -5,7 +7,6 @@ import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { SaveServer } from "@spt-aki/servers/SaveServer";
import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil";
import { Watermark } from "@spt-aki/utils/Watermark";
import { inject, injectable } from "tsyringe";
// We need to declare this class as injectable, this will add the container
// metadata
@ -15,12 +16,12 @@ export class MyCustomLauncherCallbacks extends LauncherCallbacks // <<<<=== This
{
// We need to make sure we use the constructor and pass the dependencies to the parent class!
constructor(
// @inject() will make sure to find the component with the right token and put them there
@inject("HttpResponseUtil") httpResponse: HttpResponseUtil,
// @inject() will make sure to find the component with the right token and put them there
@inject("HttpResponseUtil") httpResponse: HttpResponseUtil,
@inject("LauncherController") launcherController: LauncherController,
@inject("SaveServer") saveServer: SaveServer,
@inject("Watermark") watermark: Watermark,
@inject("WinstonLogger") private logger: ILogger
@inject("WinstonLogger") private logger: ILogger,
)
{
// Pass the parent class (LauncherCallbacks) the dependencies it needs to work
@ -28,7 +29,7 @@ export class MyCustomLauncherCallbacks extends LauncherCallbacks // <<<<=== This
}
// We override the parent method with the EXACT same signature
public override ping(url: string, info: IEmptyRequestData, sessionID: string): string
public override ping(url: string, info: IEmptyRequestData, sessionID: string): string
{
// We are overriding the parent method, so ONLY this method will run, not the parent!
// If we wanted to run both, you can always write:

View File

@ -1,4 +1,5 @@
import { DependencyContainer } from "tsyringe";
import { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod"
import { MyCustomLauncherCallbacks } from "./MyCustomLauncherCallbacks";
@ -9,7 +10,7 @@ class Mod implements IPreAkiLoadMod
// you own custom implementations the server will then use.
// In this example we will take the LauncherCallbacks class and override the ping() method
// for our own custom method that will return "Lets dance" instead of "pong!"
// Perform these actions before server fully loads
public preAkiLoad(container: DependencyContainer): void {
// Here we register our override for the component and we NEED to use the same
@ -18,14 +19,13 @@ class Mod implements IPreAkiLoadMod
// https://dev.sp-tarkov.com/SPT-AKI/Server/src/branch/development/project/src/di/Container.ts
// In this scenario we want to override LauncherCallbacks, so we find the proper registry:
//
// depContainer.register<LauncherCallbacks>("LauncherCallbacks", {useClass: LauncherCallbacks});
// depContainer.register<LauncherCallbacks>("LauncherCallbacks", { useClass: LauncherCallbacks });
//
// So what we want to do is register it with EXACTLY the same token
container.register<MyCustomLauncherCallbacks>("MyCustomLauncherCallbacks", MyCustomLauncherCallbacks);
container.register("LauncherCallbacks", {useToken: "MyCustomLauncherCallbacks"});
// Now that its registered, the server will automatically find this dependency and use it where ever its needed
container.register("LauncherCallbacks", { useToken: "MyCustomLauncherCallbacks" });
// Now that its registered, the server will automatically find this dependency and use it where ever its needed
}
}

View File

@ -17,24 +17,24 @@ export class FluentAssortConstructor
this.hashUtil = hashutil
this.logger = logger;
}
/**
* Start selling item with tpl
* @param itemTpl Tpl id of the item you want trader to sell
* @param itemId Optional - set your own Id, otherwise unique id will be generated
*/
public createSingleAssortItem(itemTpl: string, itemId = undefined): FluentAssortConstructor
public createSingleAssortItem(itemTpl: string, itemId: string = undefined): FluentAssortConstructor
{
// Create item ready for insertion into assort table
const newItemToAdd: Item = {
_id: !itemId ? this.hashUtil.generate(): itemId,
_id: itemId ?? this.hashUtil.generate(),
_tpl: itemTpl,
parentId: "hideout", // Should always be "hideout"
slotId: "hideout", // Should always be "hideout"
upd: {
UnlimitedCount: false,
StackObjectsCount: 100
}
StackObjectsCount: 100,
},
};
this.itemsToSell.push(newItemToAdd);
@ -47,10 +47,7 @@ export class FluentAssortConstructor
items[0].parentId = "hideout";
items[0].slotId = "hideout";
if (!items[0].upd)
{
items[0].upd = {}
}
items[0].upd ??= {};
items[0].upd.UnlimitedCount = false;
items[0].upd.StackObjectsCount = 100;
@ -99,14 +96,12 @@ export class FluentAssortConstructor
public addMoneyCost(currencyType: Money, amount: number): FluentAssortConstructor
{
this.barterScheme[this.itemsToSell[0]._id] = [
[
{
count: amount,
_tpl: currencyType
}
]
];
this.barterScheme[this.itemsToSell[0]._id] = [[
{
count: amount,
_tpl: currencyType,
},
]];
return this;
}
@ -121,8 +116,8 @@ export class FluentAssortConstructor
this.barterScheme[sellableItemId] = [[
{
count: count,
_tpl: itemTpl
}
_tpl: itemTpl,
},
]];
}
else
@ -139,10 +134,9 @@ export class FluentAssortConstructor
// No barter for item, add it fresh
this.barterScheme[sellableItemId][0].push({
count: count,
_tpl: itemTpl
})
_tpl: itemTpl,
});
}
}
return this;
@ -150,7 +144,7 @@ export class FluentAssortConstructor
/**
* Reset objet ready for reuse
* @returns
* @returns
*/
public export(data: ITrader): FluentAssortConstructor
{

View File

@ -12,21 +12,22 @@ import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { ITraderConfig } from "@spt-aki/models/spt/config/ITraderConfig";
import { IRagfairConfig } from "@spt-aki/models/spt/config/IRagfairConfig";
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
// New trader settings
import * as baseJson from "../db/base.json";
import { TraderHelper } from "./traderHelpers";
import { FluentAssortConstructor as FluentAssortCreator } from "./fluentTraderAssortCreator";
import { Money } from "@spt-aki/models/enums/Money";
import { Traders } from "@spt-aki/models/enums/Traders";
import { HashUtil } from "@spt-aki/utils/HashUtil";
// New trader settings
import * as baseJson from "../db/base.json";
import { TraderHelper } from "./traderHelpers";
import { FluentAssortConstructor as FluentAssortCreator } from "./fluentTraderAssortCreator";
class SampleTrader implements IPreAkiLoadMod, IPostDBLoadMod
{
private mod: string
private logger: ILogger
private traderHelper: TraderHelper
private fluentAssortCreator: FluentAssortCreator
private mod: string;
private logger: ILogger;
private traderHelper: TraderHelper;
private fluentAssortCreator: FluentAssortCreator;
constructor() {
this.mod = "13AddTrader"; // Set name of mod so we can log it to console later
@ -64,7 +65,7 @@ class SampleTrader implements IPreAkiLoadMod, IPostDBLoadMod
this.logger.debug(`[${this.mod}] preAki Loaded`);
}
/**
* Majority of trader-related work occurs after the aki database has been loaded but prior to SPT code being run
* @param container Dependency container
@ -86,39 +87,43 @@ class SampleTrader implements IPreAkiLoadMod, IPostDBLoadMod
// Add milk
const MILK_ID = "575146b724597720a27126d5"; // Can find item ids in `database\templates\items.json` or with https://db.sp-tarkov.com/search
this.fluentAssortCreator.createSingleAssortItem(MILK_ID)
.addStackCount(200)
.addBuyRestriction(10)
.addMoneyCost(Money.ROUBLES, 2000)
.addLoyaltyLevel(1)
.export(tables.traders[baseJson._id]);
this.fluentAssortCreator
.createSingleAssortItem(MILK_ID)
.addStackCount(200)
.addBuyRestriction(10)
.addMoneyCost(Money.ROUBLES, 2000)
.addLoyaltyLevel(1)
.export(tables.traders[baseJson._id]);
// Add 3x bitcoin + salewa for milk barter
const BITCOIN_ID = "59faff1d86f7746c51718c9c"
const BITCOIN_ID = "59faff1d86f7746c51718c9c";
const SALEWA_ID = "544fb45d4bdc2dee738b4568";
this.fluentAssortCreator.createSingleAssortItem(MILK_ID)
.addStackCount(100)
.addBarterCost(BITCOIN_ID, 3)
.addBarterCost(SALEWA_ID, 1)
.addLoyaltyLevel(1)
.export(tables.traders[baseJson._id]);
this.fluentAssortCreator
.createSingleAssortItem(MILK_ID)
.addStackCount(100)
.addBarterCost(BITCOIN_ID, 3)
.addBarterCost(SALEWA_ID, 1)
.addLoyaltyLevel(1)
.export(tables.traders[baseJson._id]);
// Add glock as money purchase
this.fluentAssortCreator.createComplexAssortItem(this.traderHelper.createGlock())
.addUnlimitedStackCount()
.addMoneyCost(Money.ROUBLES, 20000)
.addBuyRestriction(3)
.addLoyaltyLevel(1)
.export(tables.traders[baseJson._id]);
this.fluentAssortCreator
.createComplexAssortItem(this.traderHelper.createGlock())
.addUnlimitedStackCount()
.addMoneyCost(Money.ROUBLES, 20000)
.addBuyRestriction(3)
.addLoyaltyLevel(1)
.export(tables.traders[baseJson._id]);
// Add mp133 preset as mayo barter
this.fluentAssortCreator.createComplexAssortItem(tables.globals.ItemPresets["584148f2245977598f1ad387"]._items)
.addStackCount(200)
.addBarterCost("5bc9b156d4351e00367fbce9", 1)
.addBuyRestriction(3)
.addLoyaltyLevel(1)
.export(tables.traders[baseJson._id]);
this.fluentAssortCreator
.createComplexAssortItem(tables.globals.ItemPresets["584148f2245977598f1ad387"]._items)
.addStackCount(200)
.addBarterCost("5bc9b156d4351e00367fbce9", 1)
.addBuyRestriction(3)
.addLoyaltyLevel(1)
.export(tables.traders[baseJson._id]);
// Add trader to locale file, ensures trader text shows properly on screen
// WARNING: adds the same text to ALL locales (e.g. chinese/french/english)

View File

@ -11,6 +11,7 @@ export class TraderHelper
/**
* Add profile picture to our trader
* @param baseJson json file for trader (db/base.json)
* @param modName mod folder name
* @param preAkiModLoader mod loader class - used to get the mods file path
* @param imageRouter image router class - used to register the trader image path so we see their image on trader page
* @param traderImageName Filename of the trader icon to use
@ -19,7 +20,7 @@ export class TraderHelper
{
// Reference the mod "res" folder
const imageFilepath = `./${preAkiModLoader.getModPath(modName)}res`;
// Register a route to point to the profile picture - remember to remove the .jpg from it
imageRouter.addRoute(baseJson.avatar.replace(".jpg", ""), `${imageFilepath}/${traderImageName}`);
}
@ -38,8 +39,9 @@ export class TraderHelper
traderId: baseJson._id,
seconds: {
min: refreshTimeSecondsMin,
max: refreshTimeSecondsMax
} };
max: refreshTimeSecondsMax,
},
};
traderConfig.updateTime.push(traderRefreshRecord);
}
@ -60,15 +62,13 @@ export class TraderHelper
questassort: {
started: {},
success: {},
fail: {}
} // questassort is empty as trader has no assorts unlocked by quests
fail: {},
}, // questassort is empty as trader has no assorts unlocked by quests
};
}
/**
* Create basic data for trader + add empty assorts table for trader
* @param tables SPT db
* @param jsonUtil SPT JSON utility class
* @returns ITraderAssort
*/
private createAssortTable(): ITraderAssort
@ -78,8 +78,8 @@ export class TraderHelper
nextResupply: 0,
items: [],
barter_scheme: {},
loyal_level_items: {}
}
loyal_level_items: {},
};
return assortTable;
}
@ -96,7 +96,7 @@ export class TraderHelper
// Add the base first
glock.push({ // Add the base weapon first
_id: "glockBase", // Ids dont matter, as long as they are unique (can use hashUtil.generate() if you dont want to type every id by hand)
_tpl: "5a7ae0c351dfba0017554310" // This is the weapons tpl, found on: https://db.sp-tarkov.com/search
_tpl: "5a7ae0c351dfba0017554310", // This is the weapons tpl, found on: https://db.sp-tarkov.com/search
});
// Add barrel
@ -104,7 +104,7 @@ export class TraderHelper
_id: "glockbarrel",
_tpl: "5a6b60158dc32e000a31138b",
parentId: "glockBase", // This is a sub item, you need to define its parent its attached to / inserted into
slotId: "mod_barrel" // Required for mods, you need to define what 'role' they have
slotId: "mod_barrel", // Required for mods, you need to define what 'role' they have
});
// Add reciever
@ -112,7 +112,7 @@ export class TraderHelper
_id: "glockReciever",
_tpl:"5a9685b1a2750c0032157104",
parentId: "glockBase",
slotId: "mod_reciever"
slotId: "mod_reciever",
});
// Add compensator
@ -120,7 +120,7 @@ export class TraderHelper
_id: "glockCompensator",
_tpl:"5a7b32a2e899ef00135e345a",
parentId: "glockReciever", // The parent of this mod is the reciever NOT weapon, be careful to get the correct parent
slotId: "mod_muzzle"
slotId: "mod_muzzle",
});
// Add Pistol grip
@ -128,7 +128,7 @@ export class TraderHelper
_id: "glockPistolGrip",
_tpl:"5a7b4960e899ef197b331a2d",
parentId: "glockBase",
slotId: "mod_pistol_grip"
slotId: "mod_pistol_grip",
});
// Add front sight
@ -136,7 +136,7 @@ export class TraderHelper
_id: "glockRearSight",
_tpl: "5a6f5d528dc32e00094b97d9",
parentId: "glockReciever",
slotId: "mod_sight_rear"
slotId: "mod_sight_rear",
});
// Add rear sight
@ -144,7 +144,7 @@ export class TraderHelper
_id: "glockFrontSight",
_tpl: "5a6f58f68dc32e000a311390",
parentId: "glockReciever",
slotId: "mod_sight_front"
slotId: "mod_sight_front",
});
// Add magazine
@ -152,7 +152,7 @@ export class TraderHelper
_id: "glockMagazine",
_tpl: "630769c4962d0247b029dc60",
parentId: "glockBase",
slotId: "mod_magazine"
slotId: "mod_magazine",
});
return glock;
@ -171,7 +171,8 @@ export class TraderHelper
public addTraderToLocales(baseJson: any, tables: IDatabaseTables, fullName: string, firstName: string, nickName: string, location: string, description: string)
{
// For each language, add locale for the new trader
const locales = Object.values(tables.locales.global) as Record<string, string>[];
const locales = Object.values(tables.locales.global);
for (const locale of locales) {
locale[`${baseJson._id} FullName`] = fullName;
locale[`${baseJson._id} FirstName`] = firstName;

View File

@ -17,7 +17,7 @@ class Mod implements IPreAkiLoadMod, IPostAkiLoadMod, IPostDBLoadMod
const logger = container.resolve<ILogger>("WinstonLogger");
logger.logWithColor(`Database item table state: ${databaseServer.getTables().templates} (<<< should be undefined)`, LogTextColor.RED, LogBackgroundColor.YELLOW);
}
public postDBLoad(container: DependencyContainer): void {
// Database will be loaded, this is the fresh state of the DB so NOTHING from the AKI
// logic has modified anything yet. This is the DB loaded straight from the JSON files
@ -25,7 +25,7 @@ class Mod implements IPreAkiLoadMod, IPostAkiLoadMod, IPostDBLoadMod
const logger = container.resolve<ILogger>("WinstonLogger");
logger.logWithColor(`Database item size: ${Object.entries(databaseServer.getTables().templates.items).length}`, LogTextColor.RED, LogBackgroundColor.YELLOW);
// lets do a quick modification and see how this reflect later on, on the postAkiLoad()
// find the nvgs item by its Id
const nvgs = databaseServer.getTables().templates.items["5c0558060db834001b735271"];
// Lets log the state before the modification:

View File

@ -1,13 +1,15 @@
import { IncomingMessage, ServerResponse } from "node:http";
import { IHttpListener } from "@spt-aki/servers/http/IHttpListener";
import { IncomingMessage, ServerResponse } from "http";
export class Type2HttpListener implements IHttpListener
{
public canHandle(sessionId: string, req: IncomingMessage): boolean
public canHandle(sessionId: string, req: IncomingMessage): boolean
{
return req.method === "GET" && req.url?.includes("/type2-custom-url");
}
public handle(sessionId: string, req: IncomingMessage, resp: ServerResponse): void
public async handle(sessionId: string, req: IncomingMessage, resp: ServerResponse): Promise<void>
{
resp.writeHead(200, "OK");
resp.end("[2] This is the second example of a mod hooking into the HttpServer");

View File

@ -1,13 +1,14 @@
import { IncomingMessage, ServerResponse } from "node:http";
import { DependencyContainer } from "tsyringe";
import { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod";
import { HttpListenerModService } from "@spt-aki/services/mod/httpListener/HttpListenerModService";
import { IncomingMessage, ServerResponse } from "http";
import { Type2HttpListener } from "./Type2HttpListener";
class Mod implements IPreAkiLoadMod
{
// Code added here will load BEFORE the server has started loading
public preAkiLoad(container: DependencyContainer): void
public preAkiLoad(container: DependencyContainer): void
{
const httpListenerService = container.resolve<HttpListenerModService>("HttpListenerModService");
httpListenerService.registerHttpListener("Type1HttpListener", this.canHandleOverride, this.handleOverride)

View File

@ -1,3 +1,3 @@
{
"myProperty": "wow"
"myProperty": "wow"
}

View File

@ -1,3 +1,3 @@
{
"myProperty": "wow inside db"
"myProperty": "wow inside db"
}

View File

@ -1,3 +1,3 @@
{
"myProperty": "wow inside db inside moredb"
"myProperty": "wow inside db inside moredb"
}

View File

@ -1,25 +1,26 @@
import { DependencyContainer } from "tsyringe";
import { PreAkiModLoader } from "@spt-aki/loaders/PreAkiModLoader";
import { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ImporterUtil } from "@spt-aki/utils/ImporterUtil";
import { DependencyContainer } from "tsyringe";
import { ConfigsModelBase } from "./model/ConfigsModel";
class Mod implements IPreAkiLoadMod {
public preAkiLoad(container: DependencyContainer): void {
// get logger
const logger = container.resolve<ILogger>("WinstonLogger");
public preAkiLoad(container: DependencyContainer): void {
// get logger
const logger = container.resolve<ILogger>("WinstonLogger");
const importerUtil = container.resolve<ImporterUtil>("ImporterUtil");
const modImporter = container.resolve<PreAkiModLoader>("PreAkiModLoader");
const path = modImporter.getModPath("16ImporterUtil");
const importerUtil = container.resolve<ImporterUtil>("ImporterUtil");
const modImporter = container.resolve<PreAkiModLoader>("PreAkiModLoader");
const path = modImporter.getModPath("16ImporterUtil");
const configPath = `${path}config/`;
const configs = importerUtil.loadRecursive<ConfigsModelBase>(configPath);
logger.info(
`16ImporterUtil Configurations found: ${JSON.stringify(configs)}`,
);
}
const configPath = `${path}config/`;
const configs = importerUtil.loadRecursive<ConfigsModelBase>(configPath);
logger.info(
`16ImporterUtil Configurations found: ${JSON.stringify(configs)}`,
);
}
}
export const mod = new Mod();

View File

@ -1,17 +1,17 @@
export class ConfigsModelBase {
db: ConfigsModelDb;
config: ConfigModel;
db: ConfigsModelDb;
config: ConfigModel;
}
export class ConfigsModelDb {
moredb: ConfigsModelMoreDb;
config: ConfigModel;
moredb: ConfigsModelMoreDb;
config: ConfigModel;
}
export class ConfigsModelMoreDb {
config: ConfigModel;
config: ConfigModel;
}
export class ConfigModel {
myProperty: string;
myProperty: string;
}

View File

@ -1,3 +1,3 @@
{
"myProperty": "wow"
"myProperty": "wow"
}

View File

@ -1,3 +1,3 @@
{
"myProperty": "wow inside db"
"myProperty": "wow inside db"
}

View File

@ -1,3 +1,3 @@
{
"myProperty": "wow inside db inside moredb"
"myProperty": "wow inside db inside moredb"
}

View File

@ -1,30 +1,31 @@
import { DependencyContainer } from "tsyringe";
import { PreAkiModLoader } from "@spt-aki/loaders/PreAkiModLoader";
import { IPreAkiLoadModAsync } from "@spt-aki/models/external/IPreAkiLoadModAsync";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ImporterUtil } from "@spt-aki/utils/ImporterUtil";
import { DependencyContainer } from "tsyringe";
import { ConfigsModelBase } from "./model/ConfigsModel";
class Mod implements IPreAkiLoadModAsync {
public async preAkiLoadAsync(container: DependencyContainer): Promise<void> {
// get logger
const logger = container.resolve<ILogger>("WinstonLogger");
public async preAkiLoadAsync(container: DependencyContainer): Promise<void> {
// get logger
const logger = container.resolve<ILogger>("WinstonLogger");
const importerUtil = container.resolve<ImporterUtil>("ImporterUtil");
const modImporter = container.resolve<PreAkiModLoader>("PreAkiModLoader");
const path = modImporter.getModPath("16ImporterUtil");
const importerUtil = container.resolve<ImporterUtil>("ImporterUtil");
const modImporter = container.resolve<PreAkiModLoader>("PreAkiModLoader");
const path = modImporter.getModPath("16ImporterUtil");
const configPath = `${path}config/`;
return importerUtil
.loadAsync<ConfigsModelBase>(configPath)
.then((configs) => {
logger.info(
`17ImporterWithDependency1 Configurations found: ${JSON.stringify(
configs,
)}`,
);
});
}
const configPath = `${path}config/`;
return importerUtil
.loadAsync<ConfigsModelBase>(configPath)
.then((configs) => {
logger.info(
`17ImporterWithDependency1 Configurations found: ${JSON.stringify(
configs,
)}`,
);
});
}
}
export const mod = new Mod();

View File

@ -1,17 +1,17 @@
export class ConfigsModelBase {
db: ConfigsModelDb;
config: ConfigModel;
db: ConfigsModelDb;
config: ConfigModel;
}
export class ConfigsModelDb {
moredb: ConfigsModelMoreDb;
config: ConfigModel;
moredb: ConfigsModelMoreDb;
config: ConfigModel;
}
export class ConfigsModelMoreDb {
config: ConfigModel;
config: ConfigModel;
}
export class ConfigModel {
myProperty: string;
myProperty: string;
}

View File

@ -1,3 +1,3 @@
{
"myProperty": "wow"
"myProperty": "wow"
}

View File

@ -1,3 +1,3 @@
{
"myProperty": "wow inside db"
"myProperty": "wow inside db"
}

View File

@ -1,3 +1,3 @@
{
"myProperty": "wow inside db inside moredb"
"myProperty": "wow inside db inside moredb"
}

View File

@ -1,30 +1,31 @@
import { DependencyContainer } from "tsyringe";
import { PreAkiModLoader } from "@spt-aki/loaders/PreAkiModLoader";
import { IPreAkiLoadModAsync } from "@spt-aki/models/external/IPreAkiLoadModAsync";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ImporterUtil } from "@spt-aki/utils/ImporterUtil";
import { DependencyContainer } from "tsyringe";
import { ConfigsModelBase } from "./model/ConfigsModel";
class Mod implements IPreAkiLoadModAsync {
public async preAkiLoadAsync(container: DependencyContainer): Promise<void> {
// get logger
const logger = container.resolve<ILogger>("WinstonLogger");
public async preAkiLoadAsync(container: DependencyContainer): Promise<void> {
// get logger
const logger = container.resolve<ILogger>("WinstonLogger");
const importerUtil = container.resolve<ImporterUtil>("ImporterUtil");
const modImporter = container.resolve<PreAkiModLoader>("PreAkiModLoader");
const path = modImporter.getModPath("16ImporterUtil");
const importerUtil = container.resolve<ImporterUtil>("ImporterUtil");
const modImporter = container.resolve<PreAkiModLoader>("PreAkiModLoader");
const path = modImporter.getModPath("16ImporterUtil");
const configPath = `${path}config/`;
return importerUtil
.loadAsync<ConfigsModelBase>(configPath)
.then((configs) => {
logger.info(
`17ImporterWithDependency2 Configurations found: ${JSON.stringify(
configs,
)}`,
);
});
}
const configPath = `${path}config/`;
return importerUtil
.loadAsync<ConfigsModelBase>(configPath)
.then((configs) => {
logger.info(
`17ImporterWithDependency2 Configurations found: ${JSON.stringify(
configs,
)}`,
);
});
}
}
export const mod = new Mod();

View File

@ -1,17 +1,17 @@
export class ConfigsModelBase {
db: ConfigsModelDb;
config: ConfigModel;
db: ConfigsModelDb;
config: ConfigModel;
}
export class ConfigsModelDb {
moredb: ConfigsModelMoreDb;
config: ConfigModel;
moredb: ConfigsModelMoreDb;
config: ConfigModel;
}
export class ConfigsModelMoreDb {
config: ConfigModel;
config: ConfigModel;
}
export class ConfigModel {
myProperty: string;
myProperty: string;
}

View File

@ -8,7 +8,7 @@ import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
class Mod implements IPostDBLoadMod, IPostAkiLoadMod
{
public postDBLoad(container: DependencyContainer): void
public postDBLoad(container: DependencyContainer): void
{
// Resolve the CustomItemService container
const CustomItem = container.resolve<CustomItemService>("CustomItemService");
@ -19,13 +19,13 @@ class Mod implements IPostDBLoadMod, IPostAkiLoadMod
overrideProperties: {
Chambers: [
{
"_name": "patron_in_weapon_000",
"_id": "61f7c9e189e6fb1a5e3ea791",
"_parent": "CustomMP18",
"_props": {
"filters": [
_name: "patron_in_weapon_000",
_id: "61f7c9e189e6fb1a5e3ea791",
_parent: "CustomMP18",
_props: {
filters: [
{
"Filter": [
Filter: [
"560d5e524bdc2d25448b4571",
"5d6e6772a4b936088465b17c",
"5d6e67fba4b9361bc73bc779",
@ -41,16 +41,16 @@ class Mod implements IPostDBLoadMod, IPostAkiLoadMod
"5d6e68b3a4b9361bca7e50b5",
"5d6e6891a4b9361bd473feea",
"5d6e689ca4b9361bc8618956",
"5d6e68d1a4b93622fe60e845"
]
}
]
"5d6e68d1a4b93622fe60e845",
],
},
],
},
"_required": false,
"_mergeSlotWithChildren": false,
"_proto": "55d4af244bdc2d962f8b4571"
}
]
_required: false,
_mergeSlotWithChildren: false,
_proto: "55d4af244bdc2d962f8b4571",
},
],
}, //Overried properties basically tell the server on what data inside _props to be modified from the cloned item, in this example i am modifying the ammo used to be 12G
parentId: "5447b6094bdc2dc3278b4567", //ParentId refers to the Node item the gun will be under, you can check it in https://db.sp-tarkov.com/search
newId: "CustomMP18", //The new id of our cloned item
@ -59,21 +59,23 @@ class Mod implements IPostDBLoadMod, IPostAkiLoadMod
handbookParentId: "5b5f78e986f77447ed5636b1", //Handbook Parent Id refers to the category the gun will be under
//you see those side box tab thing that only select gun under specific icon? Handbook parent can be found in Aki_Data\Server\database\templates.
locales: {
"en": {
en: {
name: "MP-18 12g",
shortName: "Custom MP18",
description: "A custom MP18 chambered in 12G"
}
}
}
description: "A custom MP18 chambered in 12G",
},
},
};
CustomItem.createItemFromClone(ExampleCloneItem); //Basically calls the function and tell the server to add our Cloned new item into the server
}
//Check if our item is in the server or not
public postAkiLoad(container: DependencyContainer): void {
const db = container.resolve<DatabaseServer>("DatabaseServer");
const item = db.getTables().templates.items;
console.log(item["CustomMP18"]._props)
console.log(item["CustomMP18"]._props);
}
}

View File

@ -1,41 +1,41 @@
import { DependencyContainer } from 'tsyringe';
import path from "node:path";
import { DependencyContainer } from "tsyringe";
import { IPostAkiLoadMod } from "@spt-aki/models/external/IPostAkiLoadMod";
import { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { VFS } from "@spt-aki/utils/VFS";
import { jsonc } from 'jsonc';
import { jsonc } from "jsonc";
// Our dynamically imported package (via pnpm) not bundled into the server
import ora from "ora-classic";
import path from "path";
class Mod implements IPreAkiLoadMod, IPostAkiLoadMod {
public preAkiLoad(container: DependencyContainer): void {
const VFS = container.resolve<VFS>("VFS");
const logger = container.resolve<ILogger>("WinstonLogger");
public preAkiLoad(container: DependencyContainer): void {
const vfs = container.resolve<VFS>("VFS");
const logger = container.resolve<ILogger>("WinstonLogger");
const parsedJsonC = jsonc.parse(VFS.readFile(path.resolve(__dirname, "../test.jsonc")));
const parsedJsonC = jsonc.parse(vfs.readFile(path.resolve(__dirname, "../test.jsonc")));
logger.success(jsonc.stringify(parsedJsonC, null, "\t")); // you could use the built in JSON api here if you really wanted too aswell, i used jsonc to really drive home the point that it works
}
logger.success(jsonc.stringify(parsedJsonC, null, "\t")); // you could use the built in JSON api here if you really wanted too aswell, i used jsonc to really drive home the point that it works
}
public postAkiLoad(container: DependencyContainer): void {
// this first timeout is just to prevent a weird formatting problem on the console, you can ignore it, you don't really need it anyways, it's just so that it looks right on the console
setTimeout(() => {
const spinner = ora({
text: "Hacking into the mainframe...",
spinner: "line",
hideCursor: false,
discardStdin: false
}).start();
public postAkiLoad(container: DependencyContainer): void {
// this first timeout is just to prevent a weird formatting problem on the console, you can ignore it, you don't really need it anyways, it's just so that it looks right on the console
setTimeout(() => {
const spinner = ora({
text: "Hacking into the mainframe...",
spinner: "line",
hideCursor: false,
discardStdin: false
}).start();
// this second timeout is to simulate the time it takes to hack into the mainframe, as it turns out, not a lot!
setTimeout(() => {
spinner.succeed("Successfully hacked into the mainframe!");
}, 3000);
}, 1000);
}
// this second timeout is to simulate the time it takes to hack into the mainframe, as it turns out, not a lot!
setTimeout(() => {
spinner.succeed("Successfully hacked into the mainframe!");
}, 3000);
}, 1000);
}
}
export const mod = new Mod();

View File

@ -1,4 +1,4 @@
// comment
{
"data": /* comment */ "value"
"data": /* comment */ "value"
}

View File

@ -1,4 +1,5 @@
import { DependencyContainer } from "tsyringe";
import { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { LogTextColor } from "@spt-aki/models/spt/logging/LogTextColor";
@ -7,11 +8,11 @@ import { LogBackgroundColor } from "@spt-aki/models/spt/logging/LogBackgroundCol
class Mod implements IPreAkiLoadMod
{
// Code added here will load BEFORE the server has started loading
preAkiLoad(container: DependencyContainer): void
public preAkiLoad(container: DependencyContainer): void
{
// get the logger from the server container
const logger = container.resolve<ILogger>("WinstonLogger");
logger.info("I am logging info!");
logger.warning("I am logging a warning!");
logger.error("I am logging an error!");

View File

@ -1,25 +1,26 @@
import { inject, injectable } from "tsyringe";
import { IDialogueChatBot } from "@spt-aki/helpers/Dialogue/IDialogueChatBot";
import { ISendMessageRequest } from "@spt-aki/models/eft/dialog/ISendMessageRequest";
import { IUserDialogInfo } from "@spt-aki/models/eft/profile/IAkiProfile";
import { MemberCategory } from "@spt-aki/models/enums/MemberCategory";
import { MailSendService } from "@spt-aki/services/MailSendService";
import { inject, injectable } from "tsyringe";
// \/ dont forger this annotation here!
@injectable()
export class CustomChatBot implements IDialogueChatBot
{
public constructor(
constructor(
@inject("MailSendService") protected mailSendService: MailSendService,
)
{
}
{}
public getChatBot(): IUserDialogInfo
{
return {
_id: "modderBuddy",
info: {
aid: 9999999,
Info: {
Level: 1,
MemberCategory: MemberCategory.SHERPA,
Nickname: "Buddy",
@ -37,5 +38,4 @@ export class CustomChatBot implements IDialogueChatBot
);
return request.dialogId;
}
}

View File

@ -1,15 +1,15 @@
import { DependencyContainer } from 'tsyringe';
import { DependencyContainer } from "tsyringe";
import { IPostDBLoadMod } from "@spt-aki/models/external/IPostDBLoadMod";
import { DialogueController } from "@spt-aki/controllers/DialogueController";
import { CustomChatBot } from './CustomChatBot';
import { CustomChatBot } from "./CustomChatBot";
class Mod implements IPostDBLoadMod {
public postDBLoad(container: DependencyContainer): void {
public postDBLoad(container: DependencyContainer): void {
// We register and re-resolve the dependency so the container takes care of filling in the command dependencies
container.register<CustomChatBot>("CustomChatBot", CustomChatBot);
container.resolve<DialogueController>("DialogueController").registerChatBot(container.resolve<CustomChatBot>("CustomChatBot"));
}
}
}
export const mod = new Mod();

View File

@ -1,15 +1,16 @@
import { inject, injectable } from "tsyringe";
import { IChatCommand } from "@spt-aki/helpers/Dialogue/Commando/IChatCommand";
import { ISendMessageRequest } from "@spt-aki/models/eft/dialog/ISendMessageRequest";
import { IUserDialogInfo } from "@spt-aki/models/eft/profile/IAkiProfile";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { MailSendService } from "@spt-aki/services/MailSendService";
import { inject, injectable } from "tsyringe";
// \/ dont forger this annotation here!
@injectable()
export class CustomCommandoCommand implements IChatCommand
{
public constructor(
constructor(
@inject("MailSendService") protected mailSendService: MailSendService,
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
)

View File

@ -1,15 +1,15 @@
import { DependencyContainer } from 'tsyringe';
import { DependencyContainer } from "tsyringe";
import { IPostDBLoadMod } from "@spt-aki/models/external/IPostDBLoadMod";
import { CommandoDialogueChatBot } from "@spt-aki/helpers/Dialogue/CommandoDialogueChatBot";
import { CustomCommandoCommand } from './CustomCommandoCommand';
import { CustomCommandoCommand } from "./CustomCommandoCommand";
class Mod implements IPostDBLoadMod {
public postDBLoad(container: DependencyContainer): void {
public postDBLoad(container: DependencyContainer): void {
// We register and re-resolve the dependency so the container takes care of filling in the command dependencies
container.register<CustomCommandoCommand>("CustomCommandoCommand", CustomCommandoCommand);
container.resolve<CommandoDialogueChatBot>("CommandoDialogueChatBot").registerCommandoCommand(container.resolve<CustomCommandoCommand>("CustomCommandoCommand"));
}
}
}
export const mod = new Mod();

View File

@ -1,20 +1,20 @@
import { inject, injectable } from "tsyringe";
import { ISptCommand } from "@spt-aki/helpers/Dialogue/Commando/SptCommands/ISptCommand";
import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
import { MailSendService } from "@spt-aki/services/MailSendService";
import { ISendMessageRequest } from "@spt-aki/models/eft/dialog/ISendMessageRequest";
import { IUserDialogInfo } from "@spt-aki/models/eft/profile/IAkiProfile";
import { inject, injectable } from "tsyringe";
// \/ dont forger this annotation here!
@injectable()
export class CustomAkiCommand implements ISptCommand
{
public constructor(
constructor(
@inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("MailSendService") protected mailSendService: MailSendService,
)
{
}
{}
public getCommand(): string
{
@ -23,7 +23,7 @@ export class CustomAkiCommand implements ISptCommand
public getCommandHelp(): string
{
return "Usage: spt getName tplId"
return "Usage: spt getName tplId";
}
public performAction(commandHandler: IUserDialogInfo, sessionId: string, request: ISendMessageRequest): string

View File

@ -1,16 +1,15 @@
import { DependencyContainer } from 'tsyringe';
import { DependencyContainer } from "tsyringe";
import { IPostDBLoadMod } from "@spt-aki/models/external/IPostDBLoadMod";
import { SptCommandoCommands } from "@spt-aki/helpers/Dialogue/Commando/SptCommandoCommands";
import { CustomAkiCommand } from './CustomAkiCommand';
import { CustomAkiCommand } from "./CustomAkiCommand";
class Mod implements IPostDBLoadMod {
public postDBLoad(container: DependencyContainer): void {
public postDBLoad(container: DependencyContainer): void {
// We register and re-resolve the dependency so the container takes care of filling in the command dependencies
container.register<CustomAkiCommand>("CustomAkiCommand", CustomAkiCommand);
container.resolve<SptCommandoCommands>("SptCommandoCommands").registerSptCommandoCommand(container.resolve<CustomAkiCommand>("CustomAkiCommand"))
}
container.resolve<SptCommandoCommands>("SptCommandoCommands").registerSptCommandoCommand(container.resolve<CustomAkiCommand>("CustomAkiCommand"));
}
}
export const mod = new Mod();

View File

@ -1,15 +1,16 @@
import { inject, injectable } from "tsyringe";
import { IChatCommand } from "@spt-aki/helpers/Dialogue/Commando/IChatCommand";
import { ISendMessageRequest } from "@spt-aki/models/eft/dialog/ISendMessageRequest";
import { IUserDialogInfo } from "@spt-aki/models/eft/profile/IAkiProfile";
import { MailSendService } from "@spt-aki/services/MailSendService";
import { inject, injectable } from "tsyringe";
// \/ dont forger this annotation here!
@injectable()
export class AnotherCoolCommand implements IChatCommand
{
constructor(
@inject("MailSendService") protected mailSendService: MailSendService
@inject("MailSendService") protected mailSendService: MailSendService,
)
{}
@ -25,7 +26,7 @@ export class AnotherCoolCommand implements IChatCommand
return "Usage: anotherExample test";
}
}
public getCommands(): Set<string>
{
return new Set<string>(["test"]);
@ -39,5 +40,4 @@ export class AnotherCoolCommand implements IChatCommand
return request.dialogId;
}
}
}

View File

@ -1,16 +1,17 @@
import { inject, injectAll, injectable } from "tsyringe";
import { AbstractDialogueChatBot } from "@spt-aki/helpers/Dialogue/AbstractDialogueChatBot";
import { IChatCommand } from "@spt-aki/helpers/Dialogue/Commando/IChatCommand";
import { IUserDialogInfo } from "@spt-aki/models/eft/profile/IAkiProfile";
import { MemberCategory } from "@spt-aki/models/enums/MemberCategory";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { MailSendService } from "@spt-aki/services/MailSendService";
import { inject, injectAll, injectable } from "tsyringe";
// \/ dont forger this annotation here!
@injectable()
export class CustomSimpleChatBot extends AbstractDialogueChatBot
{
public constructor(
constructor(
@inject("WinstonLogger") logger: ILogger,
@inject("MailSendService") mailSendService: MailSendService,
// Remember to replace MyCommand for something unique to your mod!
@ -19,7 +20,7 @@ export class CustomSimpleChatBot extends AbstractDialogueChatBot
@injectAll("MyCommand") chatCommands: IChatCommand[],
)
{
super(logger, mailSendService, chatCommands)
super(logger, mailSendService, chatCommands);
}
public getChatBot(): IUserDialogInfo
@ -37,7 +38,6 @@ export class CustomSimpleChatBot extends AbstractDialogueChatBot
protected getUnrecognizedCommandMessage(): string
{
return "No clue what you are talking about bud!"
return "No clue what you are talking about bud!";
}
}

View File

@ -1,15 +1,16 @@
import { inject, injectable } from "tsyringe";
import { IChatCommand } from "@spt-aki/helpers/Dialogue/Commando/IChatCommand";
import { ISendMessageRequest } from "@spt-aki/models/eft/dialog/ISendMessageRequest";
import { IUserDialogInfo } from "@spt-aki/models/eft/profile/IAkiProfile";
import { MailSendService } from "@spt-aki/services/MailSendService";
import { inject, injectable } from "tsyringe";
// \/ dont forger this annotation here!
@injectable()
export class MyCoolCommand implements IChatCommand
{
constructor(
@inject("MailSendService") protected mailSendService: MailSendService
@inject("MailSendService") protected mailSendService: MailSendService,
)
{}
@ -39,5 +40,4 @@ export class MyCoolCommand implements IChatCommand
return request.dialogId;
}
}
}

View File

@ -1,13 +1,13 @@
import { DependencyContainer } from 'tsyringe';
import { DependencyContainer } from "tsyringe";
import { IPostDBLoadMod } from "@spt-aki/models/external/IPostDBLoadMod";
import { DialogueController } from "@spt-aki/controllers/DialogueController";
import { CustomSimpleChatBot } from './CustomSimpleChatBot';
import { MyCoolCommand } from './MyCoolCommand';
import { AnotherCoolCommand } from './AnotherCoolCommand';
import { CustomSimpleChatBot } from "./CustomSimpleChatBot";
import { MyCoolCommand } from "./MyCoolCommand";
import { AnotherCoolCommand } from "./AnotherCoolCommand";
class Mod implements IPostDBLoadMod {
public postDBLoad(container: DependencyContainer): void {
public postDBLoad(container: DependencyContainer): void {
// We register our commands so they get resolved by our chat bot:
container.register<MyCoolCommand>("MyCoolCommand", MyCoolCommand);
container.register<AnotherCoolCommand>("AnotherCoolCommand", AnotherCoolCommand);
@ -18,7 +18,7 @@ class Mod implements IPostDBLoadMod {
// We register and re-resolve the dependency so the container takes care of filling in the command dependencies
container.register<CustomSimpleChatBot>("CustomSimpleChatBot", CustomSimpleChatBot);
container.resolve<DialogueController>("DialogueController").registerChatBot(container.resolve<CustomSimpleChatBot>("CustomSimpleChatBot"));
}
}
}
export const mod = new Mod();

View File

@ -1,17 +1,20 @@
import { inject, injectable } from "tsyringe";
import { IAkiWebSocketMessageHandler } from "@spt-aki/servers/ws/message/IAkiWebSocketMessageHandler";
import { WebSocket, RawData } from "ws";
import { IAkiWebSocketMessageHandler } from "@spt-aki/servers/ws/message/IAkiWebSocketMessageHandler";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
// \/ dont forger this annotation here!
@injectable()
export class CustomAkiWebSocketMessageHandler implements IAkiWebSocketMessageHandler
{
constructor(@inject("WinstonLogger") protected logger: ILogger)
constructor(
@inject("WinstonLogger") protected logger: ILogger,
)
{}
public onAkiMessage(sessionID: string, client: WebSocket, message: RawData): void
{
this.logger.info(`Custom AKI WebSocket Message handler received a message for ${sessionID}: ${message.toString()}`)
this.logger.info(`Custom AKI WebSocket Message handler received a message for ${sessionID}: ${message.toString()}`);
}
}

View File

@ -1,16 +1,18 @@
import { IncomingMessage } from "node:http";
import { inject, injectable } from "tsyringe";
import { WebSocket } from "ws";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { IWebSocketConnectionHandler } from "@spt-aki/servers/ws/IWebSocketConnectionHandler";
import { IncomingMessage } from "http";
import { inject, injectable } from "tsyringe";
import { WebSocket, RawData } from "ws";
// \/ dont forger this annotation here!
@injectable()
export class CustomWebSocketConnectionHandler implements IWebSocketConnectionHandler
{
constructor(@inject("WinstonLogger") protected logger: ILogger)
{
}
constructor(
@inject("WinstonLogger") protected logger: ILogger,
)
{}
public getSocketId(): string
{
@ -25,11 +27,11 @@ export class CustomWebSocketConnectionHandler implements IWebSocketConnectionHan
public onConnection(ws: WebSocket, req: IncomingMessage): void
{
this.logger.info("Custom web socket is now connected!");
ws.on("message", (msg) =>
ws.on("message", (msg) =>
{
if (msg.toString() === "toodaloo")
{
ws.send("toodaloo back!")
ws.send("toodaloo back!");
}
});
}

View File

@ -1,11 +1,10 @@
import { DependencyContainer } from 'tsyringe';
import { DependencyContainer } from "tsyringe";
import { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod";
import { CustomWebSocketConnectionHandler } from './CustomWebSocketConnectionHandler';
import { IWebSocketConnectionHandler } from '@spt-aki/servers/ws/IWebSocketConnectionHandler';
import { CustomAkiWebSocketMessageHandler } from './CustomAkiWebSocketMessageHandler';
import { IAkiWebSocketMessageHandler } from '@spt-aki/servers/ws/message/IAkiWebSocketMessageHandler';
import { CustomWebSocketConnectionHandler } from "./CustomWebSocketConnectionHandler";
import { IWebSocketConnectionHandler } from "@spt-aki/servers/ws/IWebSocketConnectionHandler";
import { CustomAkiWebSocketMessageHandler } from "./CustomAkiWebSocketMessageHandler";
import { IAkiWebSocketMessageHandler } from "@spt-aki/servers/ws/message/IAkiWebSocketMessageHandler";
class Mod implements IPreAkiLoadMod {
public preAkiLoad(container: DependencyContainer): void {

View File

@ -8,11 +8,10 @@ import { BaseClasses } from "@spt-aki/models/enums/BaseClasses";
class Mod implements IPostDBLoadMod
{
public postDBLoad(container: DependencyContainer): void
public postDBLoad(container: DependencyContainer): void
{
// get database from server
const databaseServer = container.resolve<DatabaseServer>("DatabaseServer");
// Get all the in-memory json found in /assets/database
const tables: IDatabaseTables = databaseServer.getTables();
@ -62,4 +61,4 @@ class Mod implements IPostDBLoadMod
}
}
module.exports = { mod: new Mod() }
export const mod = new Mod();

View File

@ -1,6 +1,6 @@
import { DependencyContainer } from "tsyringe";
import { IPostAkiLoadMod } from "@spt-aki/models/external/IPostAkiLoadMod";
import { IPostAkiLoadMod } from "@spt-aki/models/external/IPostAkiLoadMod";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
@ -9,7 +9,7 @@ import { ILocationConfig } from "@spt-aki/models/spt/config/ILocationConfig";
class Mod implements IPostAkiLoadMod
{
public postAkiLoad(container: DependencyContainer): void
{
{
// get logger
const logger = container.resolve<ILogger>("WinstonLogger");
@ -21,13 +21,13 @@ class Mod implements IPostAkiLoadMod
const locationConfig: ILocationConfig = configServer.getConfig<ILocationConfig>(ConfigTypes.LOCATION);
// Log the original customs loose loot multipler
logger.info(`Here is the original customs map loose loot multipler: ${locationConfig.looseLootMultiplier.bigmap}`)
logger.info(`Here is the original customs map loose loot multipler: ${locationConfig.looseLootMultiplier.bigmap}`);
// Adjust the multipler (customs is called bigmap in bsg land)
locationConfig.looseLootMultiplier.bigmap = 10;
// Log the new multipler
logger.info(`Here is the altered customs map loose loot multipler: ${locationConfig.looseLootMultiplier.bigmap}`)
logger.info(`Here is the altered customs map loose loot multipler: ${locationConfig.looseLootMultiplier.bigmap}`);
}
}

View File

@ -1,3 +1,3 @@
{
"myProperty": "i love json5"
myProperty: "i love json5"
}

View File

@ -1,3 +1,3 @@
{
"myProperty": "i love jsonc"
"myProperty": "i love jsonc"
}

View File

@ -1,3 +1,4 @@
import path from "node:path";
import { DependencyContainer } from "tsyringe";
import { IPostAkiLoadMod } from "@spt-aki/models/external/IPostAkiLoadMod";
@ -7,10 +8,8 @@ import { VFS } from "@spt-aki/utils/VFS";
import JSON5 from "json5";
import { jsonc } from "jsonc";
import path from "path";
class Mod implements IPostAkiLoadMod
{
{
public postAkiLoad(container: DependencyContainer): void {
// get logger
const logger = container.resolve<ILogger>("WinstonLogger");

View File

@ -1,3 +1,3 @@
{
"myProperty": "wow"
"myProperty": "wow"
}

View File

@ -6,7 +6,7 @@ import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
class Mod implements IPostAkiLoadMod
{
private modConfig = require("../config/config.json");
public postAkiLoad(container: DependencyContainer): void {
// get logger
const logger = container.resolve<ILogger>("WinstonLogger");

View File

@ -1,4 +1,5 @@
import { DependencyContainer } from "tsyringe";
import { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod";
import { LauncherController } from "@spt-aki/controllers/LauncherController";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
@ -12,20 +13,20 @@ class Mod implements IPreAkiLoadMod
// ALWAYS use the container to resolve dependencies
// ****** ALWAYS *******
private static container: DependencyContainer;
// Perform these actions before server fully loads
public preAkiLoad(container: DependencyContainer): void
public preAkiLoad(container: DependencyContainer): void
{
// We will save a reference to the dependency container to resolve dependencies
// that we may need down the line
Mod.container = container;
// Wait until LauncherController gets resolved by the server and run code afterwards to replace
// Wait until LauncherController gets resolved by the server and run code afterwards to replace
// the login() function with the one below called 'replacementFunction()
container.afterResolution("LauncherController", (_t, result: LauncherController) =>
container.afterResolution("LauncherController", (_t, result: LauncherController) =>
{
// We want to replace the original method logic with something different
result.login = (info: ILoginRequestData) =>
result.login = (info: ILoginRequestData) =>
{
return this.replacementFunction(info);
}
@ -55,7 +56,7 @@ class Mod implements IPreAkiLoadMod
// We resolve 2 more dependencies: The logger and the DatabaseServer
const logger = Mod.container.resolve<ILogger>("WinstonLogger");
const dbServer = Mod.container.resolve<DatabaseServer>("DatabaseServer");
// As an example Im counting the amount of loaded items on the DB
const loadedItems = Object.entries(dbServer.getTables().templates.items).length;
// Lets do a few informational messages

View File

@ -6,8 +6,8 @@ import { MoreCode } from "./MoreCode";
class Mod implements IPostAkiLoadMod
{
public postAkiLoad(container: DependencyContainer): void
{
public postAkiLoad(container: DependencyContainer): void
{
// get logger
const logger = container.resolve<ILogger>("WinstonLogger");

View File

@ -1,24 +1,25 @@
import { DependencyContainer } from "tsyringe";
import { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod"
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"
import { OnLoadModService } from "@spt-aki/services/mod/onLoad/OnLoadModService"
import { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { OnLoadModService } from "@spt-aki/services/mod/onLoad/OnLoadModService";
class Mod implements IPreAkiLoadMod
{
public preAkiLoad(container: DependencyContainer): void
public preAkiLoad(container: DependencyContainer): void
{
const logger = container.resolve<ILogger>("WinstonLogger");
const onLoadModService = container.resolve<OnLoadModService>("OnLoadModService");
onLoadModService.registerOnLoad(
"MyCustomMod", // route key
() => this.customFunctionThatRunsOnLoad(logger),
() => this.customFunctionThatRunsOnLoad(logger),
() => "custom-mod" // new route name
)
);
}
public customFunctionThatRunsOnLoad(logger: ILogger): void
{
logger.info("MyCustomMod custom function is loading right now")
logger.info("MyCustomMod custom function is loading right now");
}
}

View File

@ -1,7 +1,8 @@
import { DependencyContainer } from "tsyringe";
import { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod"
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"
import { OnUpdateModService } from "@spt-aki/services/mod/onUpdate/OnUpdateModService"
import { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { OnUpdateModService } from "@spt-aki/services/mod/onUpdate/OnUpdateModService";
class Mod implements IPreAkiLoadMod
{
@ -10,17 +11,17 @@ class Mod implements IPreAkiLoadMod
const onUpdateModService = container.resolve<OnUpdateModService>("OnUpdateModService");
onUpdateModService.registerOnUpdate(
"MyCustomOnUpdateMod",
(timeSinceLastRun: number) => this.customFunctionThatRunsOnLoad(timeSinceLastRun, logger),
"MyCustomOnUpdateMod",
(timeSinceLastRun: number) => this.customFunctionThatRunsOnLoad(timeSinceLastRun, logger),
() => "custom-onupdate-mod" // new route name
)
);
}
public customFunctionThatRunsOnLoad(timeSinceLastRun: number, logger: ILogger): boolean
{
if (timeSinceLastRun > 30)
{
logger.info("MyCustomMod onupdate custom function is called right now")
logger.info("MyCustomMod onupdate custom function is called right now");
return true; // we did something
}

View File

@ -1,4 +1,5 @@
import { DependencyContainer } from "tsyringe";
import type { IPreAkiLoadMod } from "@spt-aki/models/external/IPreAkiLoadMod";
import type { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import type {DynamicRouterModService} from "@spt-aki/services/mod/dynamicRouter/DynamicRouterModService";
@ -10,16 +11,16 @@ class Mod implements IPreAkiLoadMod
const logger = container.resolve<ILogger>("WinstonLogger");
const dynamicRouterModService = container.resolve<DynamicRouterModService>("DynamicRouterModService");
const staticRouterModService = container.resolve<StaticRouterModService>("StaticRouterModService");
// Hook up a new dynamic route
dynamicRouterModService.registerDynamicRouter(
"MyDynamicModRouter",
[
{
url: "/my-dynamic-mod/",
action: (url, info, sessionId, output) =>
action: (url, info, sessionId, output) =>
{
logger.info("Custom dynamic route hit")
logger.info("Custom dynamic route hit");
return JSON.stringify({response: "OK"});
}
}
@ -33,9 +34,9 @@ class Mod implements IPreAkiLoadMod
[
{
url: "/my-static-route-mod/",
action: (url, info, sessionId, output) =>
action: (url, info, sessionId, output) =>
{
logger.info("Custom static route hit")
logger.info("Custom static route hit");
return JSON.stringify({response: "OK"});
}
}
@ -49,32 +50,31 @@ class Mod implements IPreAkiLoadMod
[
{
url: "/client/menu/locale/",
action: (url, info, sessionId, output) =>
action: (url, info, sessionId, output) =>
{
logger.info("/client/menu/locale/ data was: " + JSON.stringify(output))
logger.info("/client/menu/locale/ data was: " + JSON.stringify(output));
return output;
}
}
],
"aki"
);
// Hook up to existing AKI static route
staticRouterModService.registerStaticRouter(
"StaticRoutePeekingAki",
[
{
url: "/launcher/ping",
action: (url, info, sessionId, output) =>
action: (url, info, sessionId, output) =>
{
logger.info("/launcher/ping data was: " + JSON.stringify(output))
logger.info("/launcher/ping data was: " + JSON.stringify(output));
return output;
}
}
],
"aki"
);
}
}