Skip to content

Commit

Permalink
Fixes some issues reported in #1 (see commit description) Update v1.2.0
Browse files Browse the repository at this point in the history
Issues/Behaviors fixed:

 - scenario: two elements exist with the same model. one is streamed out. the script frees the model ID without verifying there is still a second element using that model, ending up resetting its model to the one defined serverside on creation

- scenario: player's skin is changed from a custom model to another custom model: script doesn't free the old skin ID used, occupying memory space

- 'player' element type was incorreclty being changed to 'ped' all the time [fix: they are two distinct types except when used in engineRequestModel argument]
  • Loading branch information
Fernando-A-Rocha committed Nov 17, 2021
1 parent 991c491 commit afe0e75
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 106 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ More info:

## Supported Types

- [x] peds (skins)
- [x] players
- [x] peds
- [x] objects
- [ ] vehicles *[coming soon]*

## Commands

- /listmods **lists all defined mods**
- /allocatedids **lists all allocated mod IDs**
- /allocatedids **shows all allocated mod IDs in realtime**
- /pedskin [ID] **creates a ped and sets their skin to a default or new ID**
- /myskin [ID] **sets your skin to a default or new ID**

Expand Down Expand Up @@ -98,7 +99,7 @@ spawnPlayer(thePlayer, x,y,z, 0, 0, int, dim) -- spawns the player in the center
setElementRotation(thePlayer,rx,ry,rz)

-- setElementModel(thePlayer, skin) -- bad!
local data_name = exports.newmodels:getDataNameFromType("ped") -- gets the correct data name
local data_name = exports.newmodels:getDataNameFromType("player") -- gets the correct data name
setElementData(thePlayer, data_name, skin) -- sets the skin ID data; clients listening for this data will apply their corresponding allocated model ID on the player
```

Expand All @@ -111,7 +112,7 @@ setElementData(thePlayer, data_name, skin) -- sets the skin ID data; clients lis
addEventHandler( "onPlayerQuit", root,
function (quitType, reason, responsibleElement)
-- local skin = getElementModel(source) -- bad!
local data_name = exports.newmodels:getDataNameFromType("ped") -- gets the correct data name
local data_name = exports.newmodels:getDataNameFromType("player") -- gets the correct data name
local skin = getElementData(source, data_name)
if skin then
-- TODO: save skin ID in the database
Expand Down
182 changes: 125 additions & 57 deletions newmodels/client.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,26 @@
/allocatedids
]]

local SEE_ALLOCATED_TABLE = true -- automatically executes /allocatedids on startup

local allocated_ids = {}

function allocatedidsCmd(cmd)
for allocated_id, v in pairs(allocated_ids) do
outputChatBox("["..v.modType.."] ID "..v.id.." allocated to ID "..v.allocated_id, 255,194,14)
end
end
addCommandHandler("allocatedids", allocatedidsCmd, false)
function allocateNewMod(elementType, id)

function allocateNewMod(modType, id)
-- /!\ doesn't take 'player' as type so we need to force that to 'ped'
local elementType2 = elementType
if elementType2 == "player" then elementType2 = "ped" end

local allocated_id = engineRequestModel(modType)
local allocated_id = engineRequestModel(elementType2)
if not allocated_id then
return false, "Failed: engineRequestModel('"..modType.."')"
return false, "Failed: engineRequestModel('"..elementType2.."')"
end

local txdpath = modsFolder..id..".txd"
local dffpath = modsFolder..id..".dff"
local colpath

if modType == "object" then
if elementType == "object" then
colpath = modsFolder..id..".col"
end

Expand Down Expand Up @@ -77,84 +76,102 @@ function allocateNewMod(modType, id)
return false, "Failed to load mod ID "..id..": dff ("..tostring(dffworked)..") txd ("..tostring(txdworked)..") "..(col and ("col ("..tostring(colworked)..")") or "")
end

allocated_ids[id] = { -- store info
modType = modType,
allocated_id = allocated_id,
}
allocated_ids[id] = allocated_id
outputDebugString("["..(eventName or "?").."] New "..elementType.." model ID "..id.." allocated to ID "..allocated_id)
return true
end

function clientSetElementCustomMod(element, modType, id)
local good, reason = verifySetModArguments(element, modType, id)
function setElementCustomModel(element, elementType, id)
local good, reason = verifySetModelArguments(element, elementType, id)
if not good then
return false, reason
end

id = tonumber(id)

-- allocate as it hasn't been done already
local allocated_info = allocated_ids[id]
if not allocated_info then
local success, reason2 = allocateNewMod(modType, id)
local allocated_id = allocated_ids[id]
if not allocated_id then
local success, reason2 = allocateNewMod(elementType, id)
if success then

-- try setting again
return clientSetElementCustomMod(element, modType, id)
return setElementCustomModel(element, elementType, id)
else
return false, reason2
end
end

setElementModel(element, allocated_info.allocated_id)
setElementModel(element, allocated_id)
return true
end

function freeElementCustomMod(modType, id)
local allocated_info = allocated_ids[id]
if not allocated_info then
function freeElementCustomMod(id)
local allocated_id = allocated_ids[id]
if not allocated_id then
return
end

local allocated_id = allocated_info.allocated_id
engineFreeModel(allocated_id)
allocated_ids[id] = nil
print("Freed allocated ID "..allocated_id.." for "..modType.." mod ID "..id)
if engineFreeModel(allocated_id) then
outputDebugString("["..(eventName or "?").."] Freed allocated ID "..allocated_id.." for mod ID "..id, 3)
else
outputDebugString("["..(eventName or "?").."] Freed allocated ID "..allocated_id.." for mod ID "..id.." but engineFreeModel returned false", 2)
end
end

function hasOtherElementsWithModel(element, id)
for elementType, name in pairs(dataNames) do
for k,el in ipairs(getElementsByType(elementType, getRootElement(), true)) do --streamed in only
if el ~= element then
if getElementData(el, name) == id then
return true
end
end
end
end
return false
end

addEventHandler( "onClientElementDataChange", root,
function (theKey, oldValue, newValue)

local modType = getDataTypeFromName(theKey)
if modType and tonumber(newValue) then
local et = getElementType(source)
if isElementTypeSupported(et) and tonumber(newValue) then

local id = tonumber(newValue)

local et = getElementType(source)

if not isElementTypeSupported(et) then
return
end

if not (modType == et) then return end -- setting model ID using the wrong name on this element

if isCustomModID(et, id) then

local success, reason = clientSetElementCustomMod(source, et, id)
local success, reason = setElementCustomModel(source, et, id)
if not success then
outputDebugString("[onClientElementDataChange] Failed clientSetElementCustomMod(source, '"..et.."', "..id.."): "..reason, 1)
outputDebugString("["..(eventName or "?").."] Failed setElementCustomModel(source, '"..et.."', "..id.."): "..reason, 1)
else
outputDebugString("[onClientElementDataChange] clientSetElementCustomMod(source, '"..et.."', "..id..") worked", 3)
outputDebugString("["..(eventName or "?").."] setElementCustomModel(source, '"..et.."', "..id..") worked", 3)
end

elseif isDefaultID(et, id) then
setElementModel(source, id)
else
outputDebugString("[onClientElementDataChange] Warning: unknown "..et.." model ID: "..id, 2)
outputDebugString("["..(eventName or "?").."] Warning: unknown "..et.." model ID: "..id, 2)
end

if tonumber(oldValue) then
local old_id = tonumber(oldValue)
local old_allocated_id = allocated_ids[old_id]
if not old_allocated_id then return end -- was not allocated

if not hasOtherElementsWithModel(source, old_id) then
freeElementCustomMod(old_id)
else
outputDebugString("["..(eventName or "?").."] Not freeing allocated ID "..old_allocated_id.." for new "..et.." model ID "..old_id,3)
return
end
end
end
end)

addEventHandler( "onClientElementStreamIn", root,
function ()
function updateStreamedInElement(source)
local et = getElementType(source)

if not isElementTypeSupported(et) then
Expand All @@ -166,27 +183,27 @@ function ()

if isCustomModID(et, id) then

local allocated_info = allocated_ids[id]
if allocated_info then return end -- ignore if already allocated:
local allocated_id = allocated_ids[id]
if allocated_id then return end -- ignore if already allocated:
-- the model only needs to be set once in onClientElementDataChange
-- when a ped/player is streamed out the model is deallocated/freed
-- note: when an element is streamed out the model is deallocated/freed

local success, reason = clientSetElementCustomMod(source, "ped", id)
local success, reason = setElementCustomModel(source, et, id)
if not success then
outputDebugString("[onClientElementStreamIn] Failed clientSetElementCustomMod(source, '"..et.."', "..id.."): "..reason, 1)
outputDebugString("["..(eventName or "?").."] Failed setElementCustomModel(source, '"..et.."', "..id.."): "..reason, 1)
else
outputDebugString("[onClientElementStreamIn] clientSetElementCustomMod(source, '"..et.."', "..id..") worked", 3)
outputDebugString("["..(eventName or "?").."] setElementCustomModel(source, '"..et.."', "..id..") worked", 3)
end

elseif isDefaultID(et, id) then
setElementModel(source, id)
else
outputDebugString("[onClientElementStreamIn] Warning: unknown "..et.." model ID: "..id, 2)
outputDebugString("["..(eventName or "?").."] Warning: unknown "..et.." model ID: "..id, 2)
end
end)
end
addEventHandler( "onClientElementStreamIn", root, function () updateStreamedInElement(source) end)

addEventHandler( "onClientElementStreamOut", root,
function ()
function updateStreamedOutElement(source)
local et = getElementType(source)

if not isElementTypeSupported(et) then
Expand All @@ -197,13 +214,64 @@ function ()
if not (id) then return end -- doesn't have a custom model

if isCustomModID(et, id) then
freeElementCustomMod("ped", id)
local allocated_id = allocated_ids[id]
if not allocated_id then return end -- was not allocated

if not hasOtherElementsWithModel(source, id) then
freeElementCustomMod(id)
else
outputDebugString("["..(eventName or "?").."] Not freeing allocated ID "..allocated_id.." for new "..et.." model ID "..id,3)
return
end
end
end)
end
addEventHandler( "onClientElementStreamOut", root, function () updateStreamedOutElement(source) end)

local drawing = false

function togSeeAllocatedTable(cmd)
if not drawing then
addEventHandler( "onClientRender", root, drawAllocatedTable)
drawing = true
else
removeEventHandler( "onClientRender", root, drawAllocatedTable)
drawing = false
end
outputChatBox("Displaying allocated_ids on screen: "..(drawing and "Yes" or "No"))
end
addCommandHandler("allocatedids", togSeeAllocatedTable, false)

local sx,sy = guiGetScreenSize()
local dfontsize = 1
local dfont = "default-bold"

function drawAllocatedTable()
local text = toJSON(allocated_ids)
local width = dxGetTextWidth(text, dfontsize, dfont)
local x,y = sx/2 - width/2, 20
dxDrawText(text, x,y,x,y, "0xffffffff", dfontsize, dfont)
end

addEventHandler( "onClientResourceStop", resourceRoot, -- free memory on stop
function (stoppedResource)
for id, v in pairs(allocated_ids) do
freeElementCustomMod(v.modType, id)
for id, allocated_id in pairs(allocated_ids) do
freeElementCustomMod(id)
end
end)

addEventHandler( "onClientResourceStart", resourceRoot,
function (startedResource)
-- search for streamed in elements with custom model ID datas
-- these were spawned in another resource and set to using custom model ID
-- we need to apply the model on them

for elementType, name in pairs(dataNames) do
for k,el in ipairs(getElementsByType(elementType, getRootElement(), true)) do
updateStreamedInElement(el)
end
end

if SEE_ALLOCATED_TABLE then
togSeeAllocatedTable()
end
end)
2 changes: 1 addition & 1 deletion newmodels/meta.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<!-- DOCUMENTATION:
https://github.com/Fernando-A-Rocha/mta-add-models
-->
<info author="Fernando" name="mta-add-models" description="minimalistic library for adding new models to your server" version="1.1" type="script"/>
<info author="Fernando" name="mta-add-models" description="minimalistic library for adding new models to your server" version="1.2.0" type="script"/>

<min_mta_version client="1.5.9-9.21026.0" server="1.5.9-9.21024.0"></min_mta_version> <!-- https://nightly.mtasa.com -->

Expand Down

1 comment on commit afe0e75

@Fernando-A-Rocha
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixes issues in #1

Please sign in to comment.