-
Posts
6,056 -
Joined
-
Last visited
-
Days Won
208
Posts posted by IIYAMA
-
-
3 hours ago, Snow-Man2 said:
leaderboard data:
It looks like you got some unparsed JSON in your JSON result.
console.log(typeof(leaderboardStats)) // If type is object: const result = JSON.parse(leaderboardStats[0]) // or if the type is a string: const result = JSON.parse(leaderboardStats)
To make things a bit easier, use back-ticks so you do not have to escape your own JSON:
local script = string.format("populateLeaderboard(`%s`);", escapedTestJSON)
-
1 hour ago, Snow-Man2 said:
its a leaderboard page and when i press F5 to see the information for each player
Which debug lines are being called in the browser? (if any)
Use the following function to open the dev tools and check which ones are visible: https://wiki.multitheftauto.com/wiki/ToggleBrowserDevTools
-
18 minutes ago, DarkStalker30 said:
It looks like correct SQL code
Are you sure those newlines are correct? As far as I can remember the following is considered a newline in Lua: \n
Or auto newlines:
local test = [[ newline newline newline newline ]]
But I can be mistaken, maybe MySQL also accepts your way.
Also make sure multi_statements is enabled, see:
- 1
-
It looks like a latency issue or bullet sync is not enabled.
Also, what is your server FPS?
The bullets here are behind the player, see screenshots.
You could also try to freeze the player with this function: https://wiki.multitheftauto.com/wiki/SetElementFrozen
And shoot from the side while running, that way you can verify if it is 1 issue or 2 issues. -
9 minutes ago, Perseus said:
I recorded another video, this time trying to compensate the latency:
What if you switch roles and film the one that gets hit? That way you can verify if it is a bug.
Also:
- Double check if bullet sync is active.
16 minutes ago, Perseus said:Is there a way to code a resource for lag compensation?
It is indeed possible to override the bullet/damage system, but lag compensation is very complex and often result in unexpected results.
-
1 hour ago, Perseus said:
As you can see in the video, the player will not get damage from the sides while sprinting
What is the ping of the other player?
It is normal to compensate your aim for latency, when the damage is registered clientside. You should aim at the location where the player is moving to. The higher the ping. the more you need to compensate.
If you shoot a player in the back/front, latency matters less, it just delays the inevitable.
Having 0 recoil makes the latency issue even worse, because the spread normally makes sure that at least some bullets hit.
I recommend to put the recoil not to 0. It is fine to put it very low, but if you put it to 0, some calculations might be miscalculated.
1 hour ago, Perseus said:I tried changing player_sync_interval from 100 to 50 but nothing.
Increasing the interval does reduce some of the extra latency. To be specific from 100 > 50 could potential update the player position 50ms faster. But that is only the case with a stable internet connection. In case of an unstable internet connection, it might even increase latency when the network is being blocked by too many unreceived position updates.
-
3 minutes ago, Spc said:
Yes, i'm using addDebugHook for detecting and blocking client-side functions as a form of anti-cheat, for example:
This function is able to mimic that behaviour you just mentioned. Just be aware.
3 minutes ago, Spc said:Do you mean adding multiple addEvent functions with same event name? I use this event in multiple resources.
It might be worthed to look in to it and test if it has any impact if any of those are restarted.
Also double check if the remote access is disabled for all of them. (2e argument of addEvent)
-
1 hour ago, Spc said:
Does anybody know what may be wrong?
Are you using addDebugHook function somewhere in your resources?
Also the addEvent function should also be called at the resource where addEventHandler is being used.
- 1
-
3 hours ago, ChvjCieObchodzi said:
do you have any methods of downloading an image by URL without using local files and using only the client side
If:
- clientside only
- without local files
It might be possible with:
fetchRemote + requestBrowserDomains
QuoteWiki:
fetchRemote
Note: Client side function only works with the server the player is connected to unless the domain has been accepted with requestBrowserDomains
But only very small images because of JSON limitations. fetchRemote only accepts JSON format. I do not recommend this method unless it is only for small avatars.
You also need an webserver/host where you save the images in JSON format and send it to the client.
-
20 hours ago, RekZ said:
it's very difficult to know which event is being continuously executed
There is not really a non expensive way except for the debughook, which is CPU expensive to use. From my perspective, the more complex you make this threshold mechanism, the faster your server will be downed.
For sensitive events attached to for example a database, you want to have some kind of firewall, for example:
checkPassiveTimer (utility with clean-up)
- 1
-
23 minutes ago, DarkStalker30 said:
if x and y and z and world and dimension and rotation then player = client setElementData(player, "player.activeMarker", false)
if x and y and z and world and dimension and rotation then local player = client -- < HERE setElementData(player, "player.activeMarker", false)
Make `player` a local variable. Else all players will share the variable `player` for the (delayed) timer functions.
You can also pass the predefined `client` variable like this:
setTimer(function (player) end, 1500, 1, client)
- 1
-
When saving the userdata of an element inside of a variable and delete the element afterwards, it's userdata type becomes generic/something else. This might also be the case for accounts.
-
45 minutes ago, Hiding said:
Only the final data that has been driven between login and quit will be saved in the db.
Purrrrfect!
- 1
-
27 minutes ago, Hiding said:
if it needs to save for example 10 players at the same time?
It depends on the amount of players and the server.
10 players should be fine.
More players? It is fine to save the the current km's inside of the memory. But writing it immediately to the database will definitely be a waste of performance, that can be spend else were.
- 1
-
4 minutes ago, Hiding said:
and I would like to save it to db when the player leaves the server to avoid unnecessary dbExec
For example trigger every 10 seconds a latent event. Those last <10 seconds do not really matter. You want to balance performance and accuracy.
To reduce server load and less db crash exploit able: Save it inside of the memory and write every 10/X minutes updates to the database. + onResourceStop
- 1
-
7 hours ago, Hiding said:
but doesn't work with the onClientPlayerQuit, why?
Multiple reasons
-
onClientPlayerQuit is for remote players only. (Other players than yourself)
- Currently when another player leaves, the stats of all other players are being saved, except yours.
-
When you quit the game, the client code on your system will stop.
- Not sure if there is enough time to send data on onClientResourceStop + resourceRoot. But I wouldn't rely on it.
- Using a timer to delay sending data, makes things really impossible.
Other issues:
-
onClientPlayerWasted should be attached to the localPlayer, not the root element. Else it also triggers for remote players.
-
addEventHandler("onClientPlayerWasted", localPlayer, onPlayerWasted)
- The source from remote events can't be trusted, use the predefined variable client instead:
-
if not exports.login:isPlayerLoggedIn(client) then return end exports.login:updatePlayerStat(client, "stat", "stat", data)
- 1
-
onClientPlayerQuit is for remote players only. (Other players than yourself)
-
On 03/08/2024 at 01:38, Patrick228 said:
through configuration files?
No, not through config files. Only resources might be able to do this.
-
21 hours ago, RekZ said:
I can't really find any useful information that works.
How about screenSource?
Here a few lines of code I copied out of my smartphone remote control app. I can vaguely remember that there were some limits I ran in to, which is why the resolution is so low. But not sure which component was affected by it -> probably the JSON format by callRemote. I probably should have used fetchRemote instead, but didn't know better at that time.
Client
myScreenSource = dxCreateScreenSource( 640 / 2, 480 / 2 ) --- ... dxUpdateScreenSource( myScreenSource ) local pixels = dxGetTexturePixels( myScreenSource ) local jpegPixels = dxConvertPixels( pixels, 'jpeg', 20 ) triggerServerEvent( "screen-stream", localPlayer, jpegPixels ) --- ...
Server
addEvent( "screen-stream", true ) addEventHandler( "screen-stream", root, function( jpegPixels ) callRemote( "http://127.0.0.1:3134/stream", result, base64Encode( jpegPixels ) ) -- end )
App
http.createServer(function (req, res) { if (req.url == "/stream") { const chunks = []; req.on('data', chunk => chunks.push(chunk)); req.on('end', () => { const data = JSON.parse(Buffer.concat(chunks).toString('utf8')); if (data != undefined && data[0] != undefined) { // I used socket IO to send the screenshots to my Phone // io.emit('broadcast', data[0].toString('base64')); // base64 image: "data:image/jpeg;base64," + data[0].toString('base64') } }) } res.end(); }).listen(3134, "127.0.0.1");
Web browser:
const image = new Image(2560, 1440); // Socket IO socket.on("broadcast", data => { image.src = "data:image/jpeg;base64," + data // Replace an image with my screen });
-
Did you know that the definition file counts up to a whopping 1364 unique functions? While the actual value might be higher or lower (missing, unknown, class/OOP or deprecated functions). Having this amount of functions in MTA is an incredible accomplish by this community.
Some other statistics:
- 125 client unique events
- 88 server unique events
- 1
-
2 hours ago, Whizz said:
When it starts and when it finds ended.
Downloads are not always done in a straight line. They are bound to resources. Even if the client finished downloading, a new resource can be started by the server admin.
Clientside:
Downloads are often busy when the transferbox is visible.
https://wiki.multitheftauto.com/wiki/IsTransferBoxVisible
According to the wiki, it is possible to forcefully show the transferbox at all times. But I doubt anybody would be willing to create a worse experience for themselves.
https://wiki.multitheftauto.com/wiki/IsTransferBoxAlwaysVisible
Serverside:
Downloads often start at https://wiki.multitheftauto.com/wiki/OnPlayerJoin
And end for each resource at:
https://wiki.multitheftauto.com/wiki/OnPlayerResourceStart
- 1
-
45 minutes ago, Mersad said:
"Note: Calls may incur a performance overhead - they are not equivalent in performance to calling functions in the same resource."
I felt that this might cause delays on the server.The code is ran single threaded so it doesn't really matter, it is not as if the CPU is doing nothing during that delay. Trigger events will move the execution to the next (server) frame, it is just delayed.
Why that extra overhead? (for events and exports)
- Another environment has to be accessed.
- The data arguments/parameters have to be cloned. If tables are passed I can imagine that will be even slower.
Here are some performance browser details + examples. Results will probably be different on your system.
I expected the events to be much worse, but that doesn't really seems to be the case. Might be because exports do also return values back to the original caller. (for better or worse)
function exportMe(a, b, c) end local thisResource = getThisResource() setTimer(function() for i = 1, 10000, 1 do call(thisResource, "exportMe", "a", "b", "c") end end, 50, 0)
Export:
58% > 60% CPUfunction exportMe(a, b, c) end addEvent("blabla", true) addEventHandler("blabla", resourceRoot, exportMe) setTimer(function() for i = 1, 10000, 1 do triggerEvent("blabla", resourceRoot, "a", "b", "c") end end, 50, 0)
Event:
62% > 64% CPUContext: This resourceRoot has no extra elements.
function exportMe(a, b, c) end addEvent("blabla", true) addEventHandler("blabla", resourceRoot, exportMe) -- 400 extra children for i = 1, 400, 1 do createElement("test") end setTimer(function() for i = 1, 10000, 1 do triggerEvent("blabla", resourceRoot, "a", "b", "c") end end, 50, 0)
Event:
67 > 68% CPUContext: Source element with 400 extra children
-
7 minutes ago, Mersad said:
If I were to use classic color placement (outputChatBox), it would be difficult, and editing the code would be harder. Is there a better way to achieve this? Additionally, if not, if I implement this with events, could it slow down or lag my server? What steps should I take to prevent this issue?
I would also go for the suggestion @FernandoMTA made, but with the syntax that works for you.
If possible move some of the events that are triggered on the same side (client/server) to export functions.
Serverside
function customOutputChatBox () -- export function -- Add rate limit here for each player -- Show message end
How to create/call export functions?
Clientside (if available)
If you use remote access.
function customOutputChatBox () -- export function -- Add rate limit here, protecting the server from spam triggerServerEvent(...) end
-
1 hour ago, Mersad said:
Is it possible that extensive use of events could cause the server to slow down or lag?
It is possible, but it depends...
There are a few ways to prevent possible lag:
- Not trigger events with the source set to root.
- Adding rate limits on clientside, to prevent (non cheating) players to abuse spamming events.
- Being aware that elementdata also triggers events.
And as TMTMTL said, some more context would be nice.
-
Resource script bundler
In some cases you want to intergrade one resource into another as if it is a kind of module. Currently we do not have many ways to create re-useable code.
But in this example/snippet I will explain how to bundle resource files. You could in theory use this as a base to make your own resource importable like DGS DX Library that does. But importing is beyond the scope of this topic.
Side note:
- This tutorial is created for programmers in mind. Some basic knowledge about Lua is required.
- Some comment lines starting with ---@, those are type annotations. Here you can find more information about those. I could have removed them, but I noticed that even without Lua Language Server some people still find them helpful.
-
Spoiler
---@alias <newType> <type> Reuse able type ---@type <type> The type of the following variable or function ---@see <type> Refering to a specific type ---@cast <variable> <type> Replacing a type ---@cast <variable> -<type> Remove a specific type of a multi type. string|number|table > -number > = string|table
- Only scripts are copied in this bundler. The other files have to be moved manually.
- While using the bundler: you need to have the debug console open, else you will not see the error/logs.
Lets get started!
The first step is to create a serverside file called bundler.lua and add it to the meta.xml of the resource you want to bundle.
<script src="bundler.lua" />
Now we have our script, we can start with writing code for opening the meta.xml file.
-- function () local metaFile = xmlLoadFile("meta.xml", true) -- true = read only if not metaFile then return end --- reading the meta.xml here ... xmlUnloadFile(metaFile) -- end
To read the meta.xml we call the function xmlNodeGetChildren, which gets all the direct children of the meta.xml root:
local nodes = xmlNodeGetChildren(metaFile) for index, node in ipairs(nodes) do -- Going through all nodes end
While going through the nodes, we need to validate if each node/file is OK to be bundled.
- We need to check if the node is a <script> node.
-
And we also do not want to bundle all files. For example, we do not want to bundle the the current bundler.lua .
By adding the attribute bundle="true", we can selective pick the correct scripts.
In this example I am using an IIFE (Immediately Invoked Function Expression), to be able to use a guard clauses inside of loops.
local nodes = xmlNodeGetChildren(metaFile) for index, node in ipairs(nodes) do (function() -- IIFE -- File validation if xmlNodeGetName(node) ~= "script" or xmlNodeGetAttribute(node, "bundle") ~= "true" then return end -- File is OK to be added to the bundle end)() end
IIFE (Immediately Invoked Function Expression)
Spoiler(function () end)()
SpoilerIIFE in loops: Not good for performance, but good for readability. In this case performance doesn't really matter.
Guard clause
Spoilerfunction test () if a == true then return end -- <<< end
If the files are bundle able, we need to:
- Get the type attribute. If none, the script is a serverside script.
-
Get and check the src (source code) attribute
- And check if the script file actually exists on the disk.
---@alias scriptTypeClient "client" ---@alias scriptTypeServer "server" ---@alias scriptTypeShared "shared" --- ... (function() if xmlNodeGetName(node) ~= "script" or xmlNodeGetAttribute(node, "bundle") ~= "true" then return end ---@type scriptTypeClient|scriptTypeServer|scriptTypeShared local fileType = xmlNodeGetAttribute(node, "type") or "server" local src = xmlNodeGetAttribute(node, "src") if not src or src == "" or not fileExists(src) then outputDebugString("File is missing, index: " .. index .. ", src:" .. tostring(src), 2) return end end)() --- ...
If that is OK, we can open the script
---@alias scriptTypeClient "client" ---@alias scriptTypeServer "server" ---@alias scriptTypeShared "shared" --- ... (function() if xmlNodeGetName(node) ~= "script" or xmlNodeGetAttribute(node, "bundle") ~= "true" then return end ---@type scriptTypeClient|scriptTypeServer|scriptTypeShared local fileType = xmlNodeGetAttribute(node, "type") or "server" local src = xmlNodeGetAttribute(node, "src") if not src or src == "" or not fileExists(src) then outputDebugString("File is missing, index: " .. index .. ", src:" .. tostring(src), 2) return end -- Here we open the script local scriptFile = fileOpen(src) if not scriptFile then outputDebugString("Unable to open file, index: " .. index .. ", src:" .. tostring(src), 2) return end fileClose(scriptFile) end)() --- ...
Example meta.xml file:
Spoiler<meta> <info author="IIYAMA" type="script" name="MTA-Communication-Enhancement" version="2.0.0" /> <min_mta_version client="1.5.4" server="1.5.4" /> <script bundle="true" src="sync/shared/eventNameObstruct.lua" type="shared" /> <script bundle="true" src="sync/shared/argumentValidation.lua" type="shared" /> <script bundle="true" src="sync/shared/callback.lua" type="shared" /> <script bundle="true" src="sync/shared/sync.lua" type="shared" /> <script bundle="true" src="sync/shared/debug.lua" type="shared" /> <script bundle="true" src="sync/server/remoteClientAccessPoints.lua" type="server" /> <script bundle="true" src="sync/server/sync.lua" type="server" /> <script bundle="true" src="sync/client/remoteClientAccessPoints.lua" type="client" /> <script bundle="true" src="sync/client/sync.lua" type="client" /> <script src="bundler/bundler.lua" type="server" /> </meta>
To bundle we need to read the files and save the content inside of memory. But some small tweaks have to be made.
---@alias scriptTypeClient "client" ---@alias scriptTypeServer "server" ---@alias scriptTypeShared "shared" ---@alias bundleType {client: string[], server: string[]} ---@type bundleType local bundleContent = { client = {}, server = {}, } --- ... (function() --- ... --- Start reading here ! local content = "do--FILE:" .. src .. "\n" .. fileRead(scriptFile, fileGetSize(scriptFile)) .. "\nend" if fileType == "shared" then ---@cast fileType -scriptTypeClient, -scriptTypeServer -- Bundle shared files in clientside and serverside to maintain startup order bundleContent.server[#bundleContent.server + 1] = content bundleContent.client[#bundleContent.client + 1] = content else ---@cast fileType -scriptTypeShared bundleContent[fileType][#bundleContent[fileType] + 1] = content end end)() --- ...
- Each file have to start with an additional do and ends with an extra end . This is will create a new block scope for each files, make sure that the local variables are not exposed in other files.
- The string "\n" represents a new line.
- The file content is saved inside of the table bundleContent.client/server. Concatenating the strings for each file might cause lag, better to do it in one go later.
Putting it together + command handler:
---@alias scriptTypeClient "client" ---@alias scriptTypeServer "server" ---@alias scriptTypeShared "shared" addCommandHandler("bundle", function(playerSource, commandName, ...) local metaFile = xmlLoadFile("meta.xml", true) if not metaFile then return end ---@alias bundleType {client: string[], server: string[]} ---@type bundleType local bundleContent = { client = {}, server = {}, } --[[ START META.XML file read ]] local nodes = xmlNodeGetChildren(metaFile) for index, node in ipairs(nodes) do (function() if xmlNodeGetName(node) ~= "script" or xmlNodeGetAttribute(node, "bundle") ~= "true" then return end ---@type scriptTypeClient|scriptTypeServer|scriptTypeShared local fileType = xmlNodeGetAttribute(node, "type") or "server" local src = xmlNodeGetAttribute(node, "src") if not src or src == "" or not fileExists(src) then outputDebugString("File is missing, index: " .. index .. ", src:" .. tostring(src), 2) return end local scriptFile = fileOpen(src) if not scriptFile then outputDebugString("Unable to open file, index: " .. index .. ", src:" .. tostring(src), 2) return end local content = "do--FILE:" .. src .. "\n" .. fileRead(scriptFile, fileGetSize(scriptFile)) .. "\nend" if fileType == "shared" then ---@cast fileType -scriptTypeClient, -scriptTypeServer -- Bundle shared files in clientside and serverside to maintain startup order bundleContent.server[#bundleContent.server + 1] = content bundleContent.client[#bundleContent.client + 1] = content else ---@cast fileType -scriptTypeShared bundleContent[fileType][#bundleContent[fileType] + 1] = content end fileClose(scriptFile) end)() end xmlUnloadFile(metaFile) end)
Functions used to bundle clientside/serverside:
local bundleFilePath = "example_bundle" .. "/" --[[ * Returns 0 if there is no file to be deleted. * Returns 1 if the file is deleted. * Returns 2 if the file is unable to be deleted. ]] ---@alias fileDeleteState 0|1|2 ---@param bundleContent bundleType The bundle ---@param typeOfFile scriptTypeServer|scriptTypeClient String 'client' or 'server ---@return boolean function createBundleFile(bundleContent, typeOfFile) local file = fileCreate(bundleFilePath .. typeOfFile .. ".lua") if not file then return false end local bundleFile = table.concat(bundleContent[typeOfFile], "\n") fileWrite(file, bundleFile) fileFlush(file) fileClose(file) return true end ---@see fileDeleteState ---@param typeOfFile scriptTypeServer|scriptTypeClient String 'client' or 'server ---@return fileDeleteState state The delete state: 0, 1, 2 function deleteBundleFile(typeOfFile) if not fileExists(bundleFilePath .. typeOfFile .. ".lua") then return 0 end return fileDelete(bundleFilePath .. typeOfFile .. ".lua") and 1 or 2 end
Functions used to generate meta.xml script lines.
local bundleFilePath = "example_bundle" .. "/" local metaFileName = "meta_fragment" --[[ * Returns 0 if there is no file to be deleted. * Returns 1 if the file is deleted. * Returns 2 if the file is unable to be deleted. ]] ---@alias fileDeleteState 0|1|2 --[[ Creates the meta fragment file, which contains the generated meta.xml script lines ]] ---@type fun(): boolean function createMetaFragment() local file = xmlCreateFile(bundleFilePath .. metaFileName .. ".xml", "meta") if not file then return false end local serverNode = xmlCreateChild(file, "script") xmlNodeSetAttribute(serverNode, "src", bundleFilePath .. "server.lua") xmlNodeSetAttribute(serverNode, "type", "server") local clientNode = xmlCreateChild(file, "script") xmlNodeSetAttribute(clientNode, "src", bundleFilePath .. "client.lua") xmlNodeSetAttribute(clientNode, "type", "client") xmlSaveFile(file) xmlUnloadFile(file) return true end --[[ Delete the meta fragment file ]] ---@see fileDeleteState ---@return fileDeleteState state The delete state: 0, 1, 2 function deleteMetaFragment() if not fileExists(bundleFilePath .. metaFileName .. ".xml") then return 0 end return fileDelete(bundleFilePath .. metaFileName .. ".xml") and 1 or 2 end
And now using the functions from above to make the bundle.
if deleteBundleFile("server") == 2 then error("Unable to replace bundle file: server.lua") elseif createBundleFile(bundleContent, "server") then outputDebugString("Created bundle file: server.lua", 4) end if deleteBundleFile("client") == 2 then error("Unable to replace bundle file: client.lua") elseif createBundleFile(bundleContent, "client") then outputDebugString("Created bundle file: client.lua", 4) end if deleteMetaFragment() == 2 then error("Unable to replace bundle file: " .. metaFileName .. ".xml") elseif createMetaFragment() then outputDebugString("Created file: " .. metaFileName .. ".xml", 4) end
This happens in the following order:
-
Delete the bundle if already exist
- If there is a problem with deleting: stop with an error. This is the point were your text editor might be blocking the deletion of the current bundle files. You need to resolve this manually.
- You need to have the debug console open, else you will not see the error/logs.
- Create bundle files
Full script:
Spoiler--[[ Add attribute bundle="true", to add files to the bundle. -- Shared <script bundle="true" src="shared.lua" type="shared" /> -- Serverside files <script bundle="true" src="server.lua" type="server" /> <script bundle="true" src="server.lua" /> -- Clientside <script bundle="true" src="client.lua" type="client" /> ]] -- The root directory of the bundles: local bundleFilePath = "example_bundle" .. "/" local metaFileName = "meta_fragment" ---@alias scriptTypeClient "client" ---@alias scriptTypeServer "server" ---@alias scriptTypeShared "shared" addCommandHandler("bundle", function(playerSource, commandName, ...) local metaFile = xmlLoadFile("meta.xml", true) if not metaFile then return end ---@alias bundleType {client: string[], server: string[]} ---@type bundleType local bundleContent = { client = {}, server = {}, } --[[ START META.XML file read ]] local nodes = xmlNodeGetChildren(metaFile) for index, node in ipairs(nodes) do (function() if xmlNodeGetName(node) ~= "script" or xmlNodeGetAttribute(node, "bundle") ~= "true" then return end ---@type scriptTypeClient|scriptTypeServer|scriptTypeShared local fileType = xmlNodeGetAttribute(node, "type") or "server" local src = xmlNodeGetAttribute(node, "src") if not src or src == "" or not fileExists(src) then outputDebugString("File is missing, index: " .. index .. ", src:" .. tostring(src), 2) return end local scriptFile = fileOpen(src) if not scriptFile then outputDebugString("Unable to open file, index: " .. index .. ", src:" .. tostring(src), 2) return end local content = "do--FILE:" .. src .. "\n" .. fileRead(scriptFile, fileGetSize(scriptFile)) .. "\nend" if fileType == "shared" then ---@cast fileType -scriptTypeClient, -scriptTypeServer -- Bundle shared files in clientside and serverside to maintain startup order bundleContent.server[#bundleContent.server + 1] = content bundleContent.client[#bundleContent.client + 1] = content else ---@cast fileType -scriptTypeShared bundleContent[fileType][#bundleContent[fileType] + 1] = content end fileClose(scriptFile) end)() end xmlUnloadFile(metaFile) --[[ END META.XML file read ]] --[[ START FILE WRITE ]] if deleteBundleFile("server") == 2 then error("Unable to replace bundle file: server.lua") elseif createBundleFile(bundleContent, "server") then outputDebugString("Created bundle file: server.lua", 4) end if deleteBundleFile("client") == 2 then error("Unable to replace bundle file: client.lua") elseif createBundleFile(bundleContent, "client") then outputDebugString("Created bundle file: client.lua", 4) end if deleteMetaFragment() == 2 then error("Unable to replace bundle file: " .. metaFileName .. ".xml") elseif createMetaFragment() then outputDebugString("Created file: " .. metaFileName .. ".xml", 4) end --[[ END FILE WRITE ]] outputDebugString("Bundle created!", 4) end) --[[ * Returns 0 if there is no file to be deleted. * Returns 1 if the file is deleted. * Returns 2 if the file is unable to be deleted. ]] ---@alias fileDeleteState 0|1|2 ---@param bundleContent bundleType The bundle ---@param typeOfFile scriptTypeServer|scriptTypeClient String 'client' or 'server ---@return boolean function createBundleFile(bundleContent, typeOfFile) local file = fileCreate(bundleFilePath .. typeOfFile .. ".lua") if not file then return false end local bundleFile = table.concat(bundleContent[typeOfFile], "\n") fileWrite(file, bundleFile) fileFlush(file) fileClose(file) return true end ---@see fileDeleteState ---@param typeOfFile scriptTypeServer|scriptTypeClient String 'client' or 'server ---@return fileDeleteState state The delete state: 0, 1, 2 function deleteBundleFile(typeOfFile) if not fileExists(bundleFilePath .. typeOfFile .. ".lua") then return 0 end return fileDelete(bundleFilePath .. typeOfFile .. ".lua") and 1 or 2 end --[[ Creates the meta fragment file, which contains the generated meta.xml script lines ]] ---@type fun(): boolean function createMetaFragment() local file = xmlCreateFile(bundleFilePath .. metaFileName .. ".xml", "meta") if not file then return false end local serverNode = xmlCreateChild(file, "script") xmlNodeSetAttribute(serverNode, "src", bundleFilePath .. "server.lua") xmlNodeSetAttribute(serverNode, "type", "server") local clientNode = xmlCreateChild(file, "script") xmlNodeSetAttribute(clientNode, "src", bundleFilePath .. "client.lua") xmlNodeSetAttribute(clientNode, "type", "client") xmlSaveFile(file) xmlUnloadFile(file) return true end --[[ Delete the meta fragment file ]] ---@see fileDeleteState ---@return fileDeleteState state The delete state: 0, 1, 2 function deleteMetaFragment() if not fileExists(bundleFilePath .. metaFileName .. ".xml") then return 0 end return fileDelete(bundleFilePath .. metaFileName .. ".xml") and 1 or 2 end
- 1
I don't know, how shared.lua file works?
in Scripting
Posted
Additional context: