Dev Update
Build & Release VS Code Extension / release (push) Failing after 18s Details

This commit is contained in:
Chase Eller 2026-03-24 21:03:43 -04:00
parent 71c201ad5c
commit c7c97e4ae5
No known key found for this signature in database
GPG Key ID: 9233D42F495E657C
28 changed files with 983 additions and 43 deletions

View File

@ -17,3 +17,7 @@ I will use this area to keep up with what i did last.
## [Release 1.0.11]
- Removed Debug Data
## [Release 1.1.0]
- Combined the 2 dev tool resources togethter to make one total tool for easier maintaining

View File

@ -8,5 +8,29 @@ A collection of custom Lua snippets for FiveM development.
- Thread templates
- Common FiveM utilities
## Usage
## Snippet Usage
Type a snippet prefix and press Ctrl+Space.
## Resource Generator Usage
This is an ***INTERNAL USE ONLY*** tool for generating a FiveM Resource using our structure that we have outlined during the GCONFIG update. This will produce you a folder with the correct naming schemes, folder structure, and settings to allow for a plug and play experience. Edit what you need to and remove/add anything you need to the provided files. If you have any updates/request for thing in the generator let Chase know so he can get it in a plugin update and get that uploaded to the repository.
Right click on your resources folder in your explorer window on the left hand side of your screen and select "Generate FiveM Resource From Template" and follow the prompts on the screen and it will create the following folder structure and fill out the fxmanifest.lua with the info provided.
```markdown
├── RESOURCE CODE
│ ├── fxmanifest.lua
│ ├── initConfig.json
│ ├── SERVER
│ │ ├── server.lua
│ ├── CLIENT
│ │ ├── client.lua
│ ├── MENUS
│ │ ├── readme.txt
```
# Disclaimer
Yes you can indeed make a resource folder yourself. Yes this is a fair bit cheating, but this ensure that the files you are starting with are a good foundation to work from and you are less likely to trip and mess up.

View File

@ -39,6 +39,7 @@ exports.activate = activate;
exports.deactivate = deactivate;
const vscode = __importStar(require("vscode"));
const updater_1 = require("./updater");
const resourceGenerator_1 = require("./resourceGenerator");
// ================= LOGGING SETUP =================
exports.updaterOutput = vscode.window.createOutputChannel("Updater Startup");
function logDebug(...args) {
@ -50,12 +51,14 @@ function logDebug(...args) {
exports.updaterOutput.show(true);
}
function activate(context) {
logDebug("[Extension] Activating extension..."); // test log
logDebug("[Extension] Activating extension...");
// Run automatic updater
(0, updater_1.runUpdater)(context);
// Register manual update check command
const disposable = vscode.commands.registerCommand("kj4lxc.cstmgames.fivem_snippets.checkUpdates", () => (0, updater_1.manualUpdateCheck)(context));
// Register manual update command
const disposable = vscode.commands.registerCommand("kj4lxc.cstmgames.fivem_devtools.autoUpdate", () => (0, updater_1.manualUpdateCheck)(context));
context.subscriptions.push(disposable);
// Register resource generator
(0, resourceGenerator_1.registerResourceGenerator)(context);
}
function deactivate() { }
//# sourceMappingURL=extension.js.map

View File

@ -1 +1 @@
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMA,4BAOC;AAED,4BAYC;AAED,gCAA+B;AA7B/B,+CAAiC;AACjC,uCAA0D;AAE1D,oDAAoD;AACvC,QAAA,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,CAAC;AAElF,SAAgB,QAAQ,CAAC,GAAG,IAAW;IACnC,MAAM,OAAO,GAAG,IAAI;SACf,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SAClE,IAAI,CAAC,GAAG,CAAC,CAAC;IACf,qBAAa,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAClC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,oCAAoC;IAC1D,qBAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC;AAED,SAAgB,QAAQ,CAAC,OAAgC;IACrD,QAAQ,CAAC,qCAAqC,CAAC,CAAC,CAAC,WAAW;IAE5D,wBAAwB;IACxB,IAAA,oBAAU,EAAC,OAAO,CAAC,CAAC;IAEpB,uCAAuC;IACvC,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,eAAe,CAC9C,8CAA8C,EAC9C,GAAG,EAAE,CAAC,IAAA,2BAAiB,EAAC,OAAO,CAAC,CACnC,CAAC;IACF,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AAC3C,CAAC;AAED,SAAgB,UAAU,KAAI,CAAC"}
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../src/extension.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOA,4BAOC;AAED,4BAeC;AAED,gCAA+B;AAjC/B,+CAAiC;AACjC,uCAA0D;AAC1D,2DAAgE;AAEhE,oDAAoD;AACvC,QAAA,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,CAAC;AAElF,SAAgB,QAAQ,CAAC,GAAG,IAAW;IACnC,MAAM,OAAO,GAAG,IAAI;SACf,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SAClE,IAAI,CAAC,GAAG,CAAC,CAAC;IACf,qBAAa,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAClC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,oCAAoC;IAC1D,qBAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC;AAED,SAAgB,QAAQ,CAAC,OAAgC;IACrD,QAAQ,CAAC,qCAAqC,CAAC,CAAC;IAEhD,wBAAwB;IACxB,IAAA,oBAAU,EAAC,OAAO,CAAC,CAAC;IAEpB,iCAAiC;IACjC,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,eAAe,CAC9C,4CAA4C,EAC5C,GAAG,EAAE,CAAC,IAAA,2BAAiB,EAAC,OAAO,CAAC,CACnC,CAAC;IACF,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAEvC,8BAA8B;IAC9B,IAAA,6CAAyB,EAAC,OAAO,CAAC,CAAC;AACvC,CAAC;AAED,SAAgB,UAAU,KAAI,CAAC"}

164
out/resourceGenerator.js Normal file
View File

@ -0,0 +1,164 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.registerResourceGenerator = registerResourceGenerator;
const vscode = __importStar(require("vscode"));
const fs_1 = require("fs");
const path = __importStar(require("path"));
let extensionTemplatePath = "";
let includeFeature = false; // ✅ boolean flag
const InputData = {
resourceCode: {
title: "Resource Code",
placeholder: "Used for the folder name: GOD, GSR, SELAPI, ...",
nextInput: "resourceName",
value: ""
},
resourceName: {
title: "Resource name",
placeholder: "B_Jobs, Phone, esx_garage, ...",
nextInput: "description",
value: ""
},
description: {
title: "Resource description",
placeholder: "This resource is ...",
nextInput: "",
value: ""
}
};
const startInput = "resourceCode";
// ================= ENTRY FUNCTION =================
function registerResourceGenerator(context) {
extensionTemplatePath = path.join(context.extensionPath, "template");
const disposable = vscode.commands.registerCommand("kj4lxc.fivem.generate-resource", (uri) => {
loadInputs(uri?.fsPath);
});
context.subscriptions.push(disposable);
}
// ================= INPUT FLOW =================
function loadInputs(resourcePath) {
for (const key in InputData) {
const field = InputData[key];
field.input = vscode.window.createInputBox();
field.input.title = field.title;
field.input.placeholder = field.placeholder;
field.input.onDidAccept(() => {
if (field.input.value.trim().length > 0) {
field.value = field.input.value;
field.input.hide();
if (field.nextInput) {
InputData[field.nextInput].input.show();
}
else {
askIncludeFeature(resourcePath); // ✅ NEW STEP
}
}
else {
vscode.window.showErrorMessage(`You need to complete this input (${field.title}).`);
}
});
if (key === startInput) {
field.input.show();
}
}
}
// ================= BOOLEAN PROMPT =================
async function askIncludeFeature(resourcePath) {
const result = await vscode.window.showQuickPick(["Yes", "No"], {
placeHolder: "Include UI Elements?"
});
if (!result) {
vscode.window.showWarningMessage("Operation cancelled.");
return;
}
includeFeature = result === "Yes";
startProcessing(resourcePath);
}
// ================= PROCESSING =================
function startProcessing(basePath) {
if (!basePath) {
vscode.window.showErrorMessage("No folder selected.");
return;
}
const folder = getGeneratedFolder("");
const resourcePath = path.join(basePath, InputData.resourceCode.value);
(0, fs_1.mkdirSync)(resourcePath);
writeFolder(resourcePath, folder);
vscode.window.showInformationMessage(`You just created a new FiveM Lua Resource named '${InputData.resourceName.value}'.`);
}
function Folder(url) {
return {
url,
files: {},
folders: {}
};
}
// ================= TEMPLATE PARSER =================
function getGeneratedFolder(url) {
const folder = Folder(url);
// ✅ SWITCH TEMPLATE BASED ON BOOLEAN
const baseTemplatePath = includeFeature
? path.join(extensionTemplatePath, "withFeature")
: path.join(extensionTemplatePath, "base");
const files = (0, fs_1.readdirSync)(path.join(baseTemplatePath, url), {
withFileTypes: true
});
for (const file of files) {
if (file.isDirectory()) {
folder.folders[file.name] = getGeneratedFolder(path.join(url, file.name));
}
else {
let content = (0, fs_1.readFileSync)(path.join(baseTemplatePath, url, file.name), "utf-8");
for (const key in InputData) {
content = content.replace(new RegExp(`\\$\\{${key}\\}`, "g"), InputData[key].value);
}
folder.files[file.name] = content;
}
}
return folder;
}
// ================= FILE WRITER =================
function writeFolder(url, folder) {
for (const key in folder.files) {
(0, fs_1.writeFileSync)(path.join(url, key), folder.files[key], "utf-8");
}
for (const key in folder.folders) {
const subFolderPath = path.join(url, key);
(0, fs_1.mkdirSync)(subFolderPath);
writeFolder(subFolderPath, folder.folders[key]);
}
}
//# sourceMappingURL=resourceGenerator.js.map

View File

@ -0,0 +1 @@
{"version":3,"file":"resourceGenerator.js","sourceRoot":"","sources":["../src/resourceGenerator.ts"],"names":[],"mappings":"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"}

View File

@ -46,7 +46,7 @@ const os = __importStar(require("os"));
const path = __importStar(require("path"));
const node_fetch_1 = __importDefault(require("node-fetch"));
// ================= LOGGING SETUP =================
exports.updaterOutput = vscode.window.createOutputChannel("Updater");
exports.updaterOutput = vscode.window.createOutputChannel("CSTMGames Updater");
function logDebug(...args) {
const message = args
.map(a => (typeof a === "object" ? JSON.stringify(a, null, 2) : a))
@ -156,29 +156,25 @@ async function installVsix(url) {
const tmpFile = path.join(os.tmpdir(), "update.vsix");
try {
logDebug("[Updater] Downloading VSIX from:", url);
// Fetch the VSIX
const res = await (0, node_fetch_1.default)(url);
if (!res.ok)
throw new Error(`HTTP ${res.status}`);
const buffer = Buffer.from(await res.arrayBuffer());
// Save to temp file
fs.writeFileSync(tmpFile, buffer);
logDebug("[Updater] VSIX saved to temp file:", tmpFile);
// Install extension from local file
await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(tmpFile));
logDebug("[Updater] Installed VSIX from temp file");
// Ask user to reload
const reload = await vscode.window.showInformationMessage("Extension updated successfully.", "Reload Now");
if (reload === "Reload Now") {
// ✅ Optional short delay (prevents race conditions)
await new Promise(resolve => setTimeout(resolve, 1500));
vscode.window.showInformationMessage("Extension updated successfully. Reloading...");
// ✅ AUTO reload VS Code (this restarts extensions)
await vscode.commands.executeCommand("workbench.action.reloadWindow");
}
}
catch (err) {
logDebug("[Updater] Install failed:", err);
vscode.window.showErrorMessage("Failed to install update.");
}
finally {
// Clean up temp file
try {
if (fs.existsSync(tmpFile)) {
fs.unlinkSync(tmpFile);

File diff suppressed because one or more lines are too long

View File

@ -1,17 +1,24 @@
{
"name": "cstmgames-fivem-snippets",
"displayName": "CSTMGames FiveM Snippets",
"description": "Snippets for FiveM",
"version": "1.0.11",
"displayName": "CSTMGames FiveM Dev Tools",
"description": "Development tools for FiveM",
"version": "1.1.0",
"author": {
"name": "Chase Eller",
"email": "chase@cstmgames.com",
"url": "https://cstmgames.com"
},
"engines": {
"vscode": "^1.110.0"
},
"main": "./out/extension.js",
"activationEvents": [
"onStartupFinished",
"onCommand:cstmgames.fivem_snippets.checkUpdates"
"onStartupFinished"
],
"icon": "pridelogo.png",
"files": [
"template/fxmanifest.lua"
],
"repository": {
"type": "git",
"url": "https://git.cstmgames.dev/CSTMGames/FiveM_Snippets.git"
@ -29,14 +36,26 @@
],
"commands": [
{
"command": "kj4lxc.cstmgames.fivem_snippets.checkUpdates",
"title": "Check for Snippet Updates"
"command": "kj4lxc.cstmgames.fivem_devtools.autoUpdate",
"title": "Check for CSTMDev Tools Updates"
},
{
"command": "kj4lxc.fivem.generate-resource",
"title": "Generate FiveM Resource"
}
],
"menus": {
"explorer/context": [
{
"when": "explorerResourceIsFolder",
"command": "kj4lxc.fivem.generate-resource"
}
]
},
"configuration": {
"title": "Snippets Updater",
"title": "CSTMGames FiveM Dev Tools Updater",
"properties": {
"cstmgames.fivem_snippets.autoUpdate": {
"cstmgames.fivem_devtools.autoUpdate": {
"type": "boolean",
"default": true,
"description": "Automatically install updates"

View File

@ -1,5 +1,6 @@
import * as vscode from "vscode";
import { runUpdater, manualUpdateCheck } from "./updater";
import { registerResourceGenerator } from "./resourceGenerator";
// ================= LOGGING SETUP =================
export const updaterOutput = vscode.window.createOutputChannel("Updater Startup");
@ -14,17 +15,20 @@ export function logDebug(...args: any[]) {
}
export function activate(context: vscode.ExtensionContext) {
logDebug("[Extension] Activating extension..."); // test log
logDebug("[Extension] Activating extension...");
// Run automatic updater
runUpdater(context);
// Register manual update check command
// Register manual update command
const disposable = vscode.commands.registerCommand(
"kj4lxc.cstmgames.fivem_snippets.checkUpdates",
"kj4lxc.cstmgames.fivem_devtools.autoUpdate",
() => manualUpdateCheck(context)
);
context.subscriptions.push(disposable);
// Register resource generator
registerResourceGenerator(context);
}
export function deactivate() {}

189
src/resourceGenerator.ts Normal file
View File

@ -0,0 +1,189 @@
import * as vscode from "vscode";
import { readFileSync, readdirSync, mkdirSync, writeFileSync } from "fs";
import * as path from "path";
let extensionTemplatePath = "";
let includeFeature = false; // ✅ boolean flag
// ================= INPUT STRUCTURE =================
interface InputField {
title: string;
placeholder: string;
nextInput: string;
value: string;
input?: vscode.InputBox;
}
const InputData: Record<string, InputField> = {
resourceCode: {
title: "Resource Code",
placeholder: "Used for the folder name: GOD, GSR, SELAPI, ...",
nextInput: "resourceName",
value: ""
},
resourceName: {
title: "Resource name",
placeholder: "B_Jobs, Phone, esx_garage, ...",
nextInput: "description",
value: ""
},
description: {
title: "Resource description",
placeholder: "This resource is ...",
nextInput: "",
value: ""
}
};
const startInput = "resourceCode";
// ================= ENTRY FUNCTION =================
export function registerResourceGenerator(context: vscode.ExtensionContext) {
extensionTemplatePath = path.join(context.extensionPath, "template");
const disposable = vscode.commands.registerCommand(
"kj4lxc.fivem.generate-resource",
(uri: vscode.Uri) => {
loadInputs(uri?.fsPath);
}
);
context.subscriptions.push(disposable);
}
// ================= INPUT FLOW =================
function loadInputs(resourcePath?: string) {
for (const key in InputData) {
const field = InputData[key];
field.input = vscode.window.createInputBox();
field.input.title = field.title;
field.input.placeholder = field.placeholder;
field.input.onDidAccept(() => {
if (field.input!.value.trim().length > 0) {
field.value = field.input!.value;
field.input!.hide();
if (field.nextInput) {
InputData[field.nextInput].input!.show();
} else {
askIncludeFeature(resourcePath); // ✅ NEW STEP
}
} else {
vscode.window.showErrorMessage(
`You need to complete this input (${field.title}).`
);
}
});
if (key === startInput) {
field.input.show();
}
}
}
// ================= BOOLEAN PROMPT =================
async function askIncludeFeature(resourcePath?: string) {
const result = await vscode.window.showQuickPick(
["Yes", "No"],
{
placeHolder: "Include UI Elements?"
}
);
if (!result) {
vscode.window.showWarningMessage("Operation cancelled.");
return;
}
includeFeature = result === "Yes";
startProcessing(resourcePath);
}
// ================= PROCESSING =================
function startProcessing(basePath?: string) {
if (!basePath) {
vscode.window.showErrorMessage("No folder selected.");
return;
}
const folder = getGeneratedFolder("");
const resourcePath = path.join(basePath, InputData.resourceCode.value);
mkdirSync(resourcePath);
writeFolder(resourcePath, folder);
vscode.window.showInformationMessage(
`You just created a new FiveM Lua Resource named '${InputData.resourceName.value}'.`
);
}
// ================= FOLDER STRUCT =================
interface FolderType {
url: string;
files: Record<string, string>;
folders: Record<string, FolderType>;
}
function Folder(url: string): FolderType {
return {
url,
files: {},
folders: {}
};
}
// ================= TEMPLATE PARSER =================
function getGeneratedFolder(url: string): FolderType {
const folder = Folder(url);
// ✅ SWITCH TEMPLATE BASED ON BOOLEAN
const baseTemplatePath = includeFeature
? path.join(extensionTemplatePath, "withFeature")
: path.join(extensionTemplatePath, "base");
const files = readdirSync(path.join(baseTemplatePath, url), {
withFileTypes: true
});
for (const file of files) {
if (file.isDirectory()) {
folder.folders[file.name] = getGeneratedFolder(
path.join(url, file.name)
);
} else {
let content = readFileSync(
path.join(baseTemplatePath, url, file.name),
"utf-8"
);
for (const key in InputData) {
content = content.replace(
new RegExp(`\\$\\{${key}\\}`, "g"),
InputData[key].value
);
}
folder.files[file.name] = content;
}
}
return folder;
}
// ================= FILE WRITER =================
function writeFolder(url: string, folder: FolderType) {
for (const key in folder.files) {
writeFileSync(path.join(url, key), folder.files[key], "utf-8");
}
for (const key in folder.folders) {
const subFolderPath = path.join(url, key);
mkdirSync(subFolderPath);
writeFolder(subFolderPath, folder.folders[key]);
}
}

View File

@ -7,7 +7,7 @@ import fetch from "node-fetch";
// ================= LOGGING SETUP =================
export const updaterOutput = vscode.window.createOutputChannel("Updater");
export const updaterOutput = vscode.window.createOutputChannel("CSTMGames Updater");
export function logDebug(...args: any[]) {
const message = args
@ -152,38 +152,35 @@ async function installVsix(url: string) {
try {
logDebug("[Updater] Downloading VSIX from:", url);
// Fetch the VSIX
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const buffer = Buffer.from(await res.arrayBuffer());
// Save to temp file
fs.writeFileSync(tmpFile, buffer);
logDebug("[Updater] VSIX saved to temp file:", tmpFile);
// Install extension from local file
await vscode.commands.executeCommand(
"workbench.extensions.installExtension",
vscode.Uri.file(tmpFile)
);
logDebug("[Updater] Installed VSIX from temp file");
// Ask user to reload
const reload = await vscode.window.showInformationMessage(
"Extension updated successfully.",
"Reload Now"
// ✅ Optional short delay (prevents race conditions)
await new Promise(resolve => setTimeout(resolve, 1500));
vscode.window.showInformationMessage(
"Extension updated successfully. Reloading..."
);
if (reload === "Reload Now") {
// ✅ AUTO reload VS Code (this restarts extensions)
await vscode.commands.executeCommand("workbench.action.reloadWindow");
}
} catch (err) {
logDebug("[Updater] Install failed:", err);
vscode.window.showErrorMessage("Failed to install update.");
} finally {
// Clean up temp file
try {
if (fs.existsSync(tmpFile)) {
fs.unlinkSync(tmpFile);

View File

@ -0,0 +1,6 @@
If you need to create a menu please add it here in this folder then declare it properly in the fxmanifest.
add "menuv" to your dependencies
add '@menuv/menuv.lua' and 'menu/*.lua' to your client_scripts declaration.

View File

@ -0,0 +1,49 @@
--GLOBAL CONSTANTS
local CLASS = "CHANGEME"--This is the name that the logging framework will use to display your logs in the correct manner
local LOG_LEVEL = GetResourceMetadata(GetCurrentResourceName(), "log_level", 0)
local RESOURCE_CODE = GetResourceMetadata(GetCurrentResourceName(), "resource_code", 0)
--LOCAL VARIABLES
--END CONFIG
-----------------------------------------------------------------------------------------------
-------------------
--YOUR CODE HERE---
--HAPPY CODING!----
-------------------
------------------------------------------------------------------------------------------------
--SLF Server Logging Function DO NOT TOUCH unless you know what you are doing
---Used to send logs to the client console
---@param message any Preformatted message or VARIABLES to be sent to log
---@param logLevel integer Level of log, 1-Error, 2-Warn, 3-Info, 4-Debug
function Log(message, logLevel)
local line = debug.getinfo(2, "l").currentline
local name = debug.getinfo(2, "n").name
if name == "fn" then
name = "Thread"
end
local value = "{"..CLASS.."."..name.."("..line..")} "..message
if logLevel then
if tonumber(LOG_LEVEL) >= logLevel then
exports.SLF:LogToClient(RESOURCE_CODE, logLevel, value)
end
else
exports.SLF:LogToClient(RESOURCE_CODE, 4, value)
Log("Warning line was logged without a level value... Logged it as debug as that is the safest route.", 1)
end
end
function GetSID(UUID)
Log("Requesting SID for UUID: "..UUID, 4)
local SID = exports.SPH:GetSID(UUID)
Log("Retreived SID: "..SID.." for UUID: "..UUID, 4)
return SID
end

View File

@ -0,0 +1,36 @@
fx_version 'adamant'
game 'gta5'
name '${resourceName}'
description '${description}'
author 'CSTMChristina & KJ4LXC'
version '0.0.1'
--this is a working script
resource_code '${resourceCode}'
log_level '4'
bucket_prefix '9'
dependencies {
'SLF',
'SCF'
}
files {
'client/config/*.json'
}
-- shared_scripts {
-- 'shared/*.lua'
-- }
client_scripts {
'@menuv/menuv.lua',
'menus/*.lua',
'client/*.lua'
}
server_scripts {
'server/*.lua'
}

View File

@ -0,0 +1,59 @@
--GLOBAL CONSTANTS
local CLASS = "CHANGEME"--This is the name that the logging framework will use to display your logs in the correct manner
local LOG_LEVEL = GetResourceMetadata(GetCurrentResourceName(), "log_level", 0)
local RESOURCE_CODE = GetResourceMetadata(GetCurrentResourceName(), "resource_code", 0)
--Global Variables
--LOCAL VARIABLES
--END CONFIG
-----------------------------------------------------------------------------------------------
-------------------
--YOUR CODE HERE---
--HAPPY CODING!----
-------------------
------------------------------------------------------------------------------------------------
--SLF Server Logging Function DO NOT TOUCH unless you know what you are doing.
---Used to send logs to the server console
---@param message any Preformatted message or VARIABLES to be sent to log
---@param logLevel integer Level of log, 1-Error, 2-Warn, 3-Info, 4-Debug
function Log(message, logLevel)
local line = debug.getinfo(2, "l").currentline
local name = debug.getinfo(2, "n").name
if name == "fn" then
name = "Thread"
end
local value = "{"..CLASS.."."..name.."("..line..")} "..message
if logLevel then
if tonumber(LOG_LEVEL) >= logLevel then
exports.SLF:LogToServer(RESOURCE_CODE, logLevel, value)
end
else
exports.SLF:LogToServer(RESOURCE_CODE, 4, value)
Log("Warning line was logged without a level value... Logged it as debug as that is the safest route.", 1)
end
end
function DisLog(message)
local line = debug.getinfo(2, "l").currentline
local name = debug.getinfo(2, "n").name
if name == "fn" then
name = "Thread"
end
local value = "{"..CLASS.."."..name.."("..line..")} "..message
exports.SLF:LogServerToDiscord(RESOURCE_CODE, message)
end
function GetSID(UUID)
Log("Requesting SID for UUID: "..UUID, 4)
local SID = exports.SPH:GetSID(UUID)
Log("Retreived SID: "..SID.." for UUID: "..UUID, 4)
return SID
end

View File

View File

@ -0,0 +1,6 @@
If you need to create a menu please add it here in this folder then declare it properly in the fxmanifest.
add "menuv" to your dependencies
add '@menuv/menuv.lua' and 'menu/*.lua' to your client_scripts declaration.

View File

@ -0,0 +1,49 @@
--GLOBAL CONSTANTS
local CLASS = "CHANGEME"--This is the name that the logging framework will use to display your logs in the correct manner
local LOG_LEVEL = GetResourceMetadata(GetCurrentResourceName(), "log_level", 0)
local RESOURCE_CODE = GetResourceMetadata(GetCurrentResourceName(), "resource_code", 0)
--LOCAL VARIABLES
--END CONFIG
-----------------------------------------------------------------------------------------------
-------------------
--YOUR CODE HERE---
--HAPPY CODING!----
-------------------
------------------------------------------------------------------------------------------------
--SLF Server Logging Function DO NOT TOUCH unless you know what you are doing
---Used to send logs to the client console
---@param message any Preformatted message or VARIABLES to be sent to log
---@param logLevel integer Level of log, 1-Error, 2-Warn, 3-Info, 4-Debug
function Log(message, logLevel)
local line = debug.getinfo(2, "l").currentline
local name = debug.getinfo(2, "n").name
if name == "fn" then
name = "Thread"
end
local value = "{"..CLASS.."."..name.."("..line..")} "..message
if logLevel then
if tonumber(LOG_LEVEL) >= logLevel then
exports.SLF:LogToClient(RESOURCE_CODE, logLevel, value)
end
else
exports.SLF:LogToClient(RESOURCE_CODE, 4, value)
Log("Warning line was logged without a level value... Logged it as debug as that is the safest route.", 1)
end
end
function GetSID(UUID)
Log("Requesting SID for UUID: "..UUID, 4)
local SID = exports.SPH:GetSID(UUID)
Log("Retreived SID: "..SID.." for UUID: "..UUID, 4)
return SID
end

View File

@ -0,0 +1,57 @@
body {
font-size: 20px;
text-align: center;
}
.unselectable {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
.loader {
border: 16px solid #6d6d6dc4; /* Light grey */
border-top: 16px solid #4f2580; /* purple */
border-radius: 50%;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
right: 1%;
bottom: 1%;
position: fixed;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.fade-in-image {
animation: fadeIn 1.5S;
-webkit-animation: fadeIn 1.5S;
-moz-animation: fadeIn 1.5S;
-o-animation: fadeIn 1.5S;
-ms-animation: fadeIn 1.5S;
}
@keyframes fadeIn {
0% { opacity: 0; }
100% { opacity: 1; }
}
.fade-out-image {
animation: fadeOut 1.5S;
-webkit-animation: fadeOut 1.5S;
-moz-animation: fadeOut 1.5S;
-o-animation: fadeOut 1.5S;
-ms-animation: fadeOut 1.5S;
}
@keyframes fadeOut {
0% { opacity: 1; }
100% { opacity: 0; }
}

View File

@ -0,0 +1,41 @@
<html>
<head>
<link rel="stylesheet" href="reset.css" type="text/css">
<link rel="stylesheet" href="loadsplash.css" type="text/css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fjalla+One&family=Inconsolata:wght@300&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Fjalla+One&family=Inconsolata:wght@300&family=Roboto+Condensed&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<script src="nui://game/ui/jquery.js" type="text/javascript"></script>
<script src="nui://game/ui/jquery.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/howler/2.1.1/howler.min.js" type="text/javascript"></script>
<script>
var audioPlayer = null;
// Listen for NUI Messages.
window.addEventListener('message', function(event) {
// Check for playSound transaction
if (event.data.transactionType == "playSound") {
if (audioPlayer != null) {
audioPlayer.pause();
}
audioPlayer = new Howl({src: ["./sounds/" + event.data.transactionFile + ".ogg"]});
audioPlayer.volume(event.data.transactionVolume);
audioPlayer.play();
}
});
</script>
</head>
<body id="splashScreen" class="unselectable w3-display-container">
<div class="w3-animate-opacity">
<script src="./loadsplash.js" type="text/javascript"></script>
<div id="loader" class="loader"></div>
<div class="fade-in-image">
<img id="bgIMG" src="https://images.cstm.games/imgstor/splashscreens/defaultsplash.png" alt="" style="width:100%;height:100%;">
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,47 @@
$(function () {
function showSplash(bool) {
if (bool) {
$("#splashScreen").show();
} else {
$("#splashScreen").hide();
}
}
function showLoadingSplash(bool) {
if (bool) {
$("#splashScreen").show();
$("#loader").show();
} else {
$("#splashScreen").hide();
$("#loader").hide();
}
}
window.addEventListener('message', function(event) {
var item = event.data;
// Show or hide the splash screen without loading spinner
if (item.type === "showSplash") {
if (item.status == true) {
showSplash(true)
} else {
showSplash(false)
}
}
// Show or hide the loading splash screen
if (item.type === "showLoadingSplash") {
if (item.status == true) {
showLoadingSplash(true)
} else {
showLoadingSplash(false)
}
}
if (item.type === "setIMG") {
document.getElementById("bgIMG").src = item.value;
}
})
showLoadingSplash(false)
})

View File

@ -0,0 +1,48 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

Binary file not shown.

View File

@ -0,0 +1,38 @@
local RESOURCE_CODE = GetResourceMetadata(GetCurrentResourceName(), "resource_code", 0)
local SPLASH_IMG = GetResourceMetadata(GetCurrentResourceName(), "SPLASH_IMG", 0)
--Setup the splash image on script load
Citizen.CreateThread(function()
print("Setting Up LoadSplash")
SendNUIMessage({
type = "setIMG",
value = SPLASH_IMG,
})
end)
-- Show and hide the splash without spinner
function displaySplash(bool)
SendNUIMessage({
type = "showSplash",
status = bool,
})
end
RegisterNetEvent(RESOURCE_CODE..':ShowStaticSplash')
AddEventHandler(RESOURCE_CODE..':ShowStaticSplash', function(bool)
displaySplash(bool)
end)
-- Show and hide the splash with spinner
function displayLoadSplash(bool)
SendNUIMessage({
type = "showLoadingSplash",
status = bool,
})
end
RegisterNetEvent(RESOURCE_CODE..':ShowLoadingSplash')
AddEventHandler(RESOURCE_CODE..':ShowLoadingSplash', function(bool)
displayLoadSplash(bool)
end)

View File

@ -0,0 +1,44 @@
fx_version 'adamant'
game 'gta5'
name '${resourceName}'
description '${description}'
author 'CSTMChristina & KJ4LXC'
version '0.0.1'
--this is a working script
resource_code '${resourceCode}'
log_level '4'
bucket_prefix '9'
SPLASH_IMG 'https://images.cstm.games/imgstor/splashscreens/defaultsplash.png'
dependencies {
'SLF',
'SCF'
}
files {
'client/html/index.html',
'client/html/sounds/*.ogg',
'client/config/*.json',
'client/html/loadsplash.html',
'client/html/loadsplash.js',
'client/html/loadsplash.css',
'client/html/reset.css'
}
ui_page 'client/html/loadsplash.html'
-- shared_scripts {
-- 'shared/*.lua'
-- }
client_scripts {
'@menuv/menuv.lua',
'menus/*.lua',
'client/*.lua'
}
server_scripts {
'server/*.lua'
}

View File

@ -0,0 +1,59 @@
--GLOBAL CONSTANTS
local CLASS = "CHANGEME"--This is the name that the logging framework will use to display your logs in the correct manner
local LOG_LEVEL = GetResourceMetadata(GetCurrentResourceName(), "log_level", 0)
local RESOURCE_CODE = GetResourceMetadata(GetCurrentResourceName(), "resource_code", 0)
--Global Variables
--LOCAL VARIABLES
--END CONFIG
-----------------------------------------------------------------------------------------------
-------------------
--YOUR CODE HERE---
--HAPPY CODING!----
-------------------
------------------------------------------------------------------------------------------------
--SLF Server Logging Function DO NOT TOUCH unless you know what you are doing.
---Used to send logs to the server console
---@param message any Preformatted message or VARIABLES to be sent to log
---@param logLevel integer Level of log, 1-Error, 2-Warn, 3-Info, 4-Debug
function Log(message, logLevel)
local line = debug.getinfo(2, "l").currentline
local name = debug.getinfo(2, "n").name
if name == "fn" then
name = "Thread"
end
local value = "{"..CLASS.."."..name.."("..line..")} "..message
if logLevel then
if tonumber(LOG_LEVEL) >= logLevel then
exports.SLF:LogToServer(RESOURCE_CODE, logLevel, value)
end
else
exports.SLF:LogToServer(RESOURCE_CODE, 4, value)
Log("Warning line was logged without a level value... Logged it as debug as that is the safest route.", 1)
end
end
function DisLog(message)
local line = debug.getinfo(2, "l").currentline
local name = debug.getinfo(2, "n").name
if name == "fn" then
name = "Thread"
end
local value = "{"..CLASS.."."..name.."("..line..")} "..message
exports.SLF:LogServerToDiscord(RESOURCE_CODE, message)
end
function GetSID(UUID)
Log("Requesting SID for UUID: "..UUID, 4)
local SID = exports.SPH:GetSID(UUID)
Log("Retreived SID: "..SID.." for UUID: "..UUID, 4)
return SID
end

View File