Jump to content

Can't index java class fields from lua userdata.


Kenneth

Recommended Posts

When I get the player class getPlayer() then I want to index a field it gives me nil.

Why can't I index fields from class but only methods?

Example

The java IsoPlayer closestZombie field in a public float But in a lua script below

getPlayer().closestZombie --Gives me nil

 

Link to comment
Share on other sites

A Solution I found was

--Doesn't work on private java fields

local indexOfField = 21 --CanHearAll
local player = getPlayer()
local field = getClassField(player,indexOfField)
local value = getClassFieldVal(player, field)
print(value)

 

To get the index of field 0 then count in the java class files until you reach your field I wrote a function that does this for you and get field

local function getField(class, fieldName)
    local i = 0
    while true do
        local field = getClassField(class,i)
        if field:getName() == fieldName then
            return getClassFieldVal(class,field)
        end
        i = i + 1
    end
end
 
print(getField(getPlayer(), "closestZombie"))
Edited by Kenneth
Link to comment
Share on other sites

11 hours ago, Kenneth said:

A Solution I found was

--Doesn't work on private java fields

local indexOfField = 21 --CanHearAll
local player = getPlayer()
local field = getClassField(player,indexOfField)
local value = getClassFieldVal(player, field)
print(value)

 

To get the index of field 0 then count in the java class files until you reach your field I wrote a function that does this for you and get field

local function getField(class, fieldName)
    local i = 0
    while true do
        local field = getClassField(class,i)
        if field:getName() == fieldName then
            return getClassFieldVal(class,field)
        end
        i = i + 1
    end
end
 
print(getField(getPlayer(), "closestZombie"))

 

The code could quickly become inoperable with updates. At least that hardcoded value of 21.

I took a quick look at the Java classes, maybe one of this methods might solve your problem as well

---@public
---@param player int
---@return IsoZombie
function IsoCell:getNearestVisibleZombie(player) end

 

---@public
---@return ArrayList
function IsoCell:getZombieList() end

 

 

Edited by stuck1a
Link to comment
Share on other sites

3 hours ago, Kenneth said:

Hey @stuck1a thanks for the reply I think it would be best to use the methods if you find the method like you showed. 

But where I got stuck was at IsoChunk.vehicles I was unable to find a method to get me the vehicles in an iso chunk.

 

Since I'm at office, I can't use my IDE to search for exposed methods, but regarding to the online docs, IsoCell provide you with that one at least:

public ArrayList<BaseVehicle> getVehicles()

 

Which means you have to calc the distance youself for each entry and find the nearest vehicle. I think luautils contains a method which will do that for you, otherwise you might calculate it yourself by using pythagorean theorem a²+b²=c² or in this context: distance = sqrt( (x_vehicle - x_plr)² + (y_vehicle - y_plr)² )

 

// Honestly, your solution will be faster since in this one, you have to do calculations on lua level which have already been done in Java. It therefore depends entirely on the context whether you give priority to performance or if you prefer to check with each update whether the code still works or not and correct it if necessary.

Edited by stuck1a
added note
Link to comment
Share on other sites

20 minutes ago, stuck1a said:

 

Since I'm at office, I can't use my IDE to search for exposed methods, but regarding to the online docs, IsoCell provide you with that one at least:

public ArrayList<BaseVehicle> getVehicles()

 

Which means you have to calc the distance youself for each entry and find the nearest vehicle. I think luautils contains a method which will do that for you, otherwise you might calculate it yourself by using pythagorean theorem a²+b²=c² or in this context: distance = sqrt( (x_vehicle - x_plr)² + (y_vehicle - y_plr)² )

 

// Honestly, your solution will be faster since in this one, you have to do calculations on lua level which have already been done in Java. It therefore depends entirely on the context whether you give priority to performance or if you prefer to check with each update whether the code still works or not and correct it if necessary.

So I have thought that the reason I don't want to use that because if you are close to the edge of a cell won't it disregard vehicles in adjacent cells. What I currently am doing is getting IsoChunk around player and then get vehicles from those chunks.

Link to comment
Share on other sites

3 hours ago, Kenneth said:

So I have thought that the reason I don't want to use that because if you are close to the edge of a cell won't it disregard vehicles in adjacent cells. What I currently am doing is getting IsoChunk around player and then get vehicles from those chunks.

 

I was afraid that would be a problem (since I couldn't imagine sth where only the cell matters).

Does that mean you need a solution that returns the nearest IsoObject of type XY, regardless of cells/chunks?.

 

Depending on the maximum distance you want to check, the following solutions would spontaneously come to mind:

- Check all squares around player position (e.g. circular with increasing radius) until you reached your maximum distance.

  But honestly, even if you do this as performant as possible, this is a solution I wouldn't recommend, if you have to execute this task very frequently or if your maximum radius is greater than 20-30 Squares from player position. (for r=30 squares there would be already (up to) 900 IsoSquares to check - if course only in worst case, that there isn't any target object within this radius)

 

- Use the above functions, but check whether your search radius requires the data of one or more other cells/chunks and, if necessary, merge their object lists before iterating over them to find the nearest object.

 

I personally would prefer method 2, if possible, since it's the one which is way more performant while staying as failsafe as possible. But if checking your mod after updates is not a problem for you, then your current solution is of course also an option.

 

 

// Btw:

Would you mind, if I use this nudge to implement something like

FindNearestIsoObject(int x, int y, int z, bool fixZ = true, isoType = 'IsoThumpable'): IsoObject|false

in my framworks util class? Since I'm pretty sure, I will require such an function as well sooner or later for my current project.

Edited by stuck1a
Link to comment
Share on other sites

5 hours ago, stuck1a said:

 

I was afraid that would be a problem (since I couldn't imagine sth where only the cell matters).

Does that mean you need a solution that returns the nearest IsoObject of type XY, regardless of cells/chunks?.

 

Depending on the maximum distance you want to check, the following solutions would spontaneously come to mind:

- Check all squares around player position (e.g. circular with increasing radius) until you reached your maximum distance.

  But honestly, even if you do this as performant as possible, this is a solution I wouldn't recommend, if you have to execute this task very frequently or if your maximum radius is greater than 20-30 Squares from player position. (for r=30 squares there would be already (up to) 900 IsoSquares to check - if course only in worst case, that there isn't any target object within this radius)

 

- Use the above functions, but check whether your search radius requires the data of one or more other cells/chunks and, if necessary, merge their object lists before iterating over them to find the nearest object.

 

I personally would prefer method 2, if possible, since it's the one which is way more performant while staying as failsafe as possible. But if checking your mod after updates is not a problem for you, then your current solution is of course also an option.

 

 

// Btw:

Would you mind, if I use this nudge to implement something like

FindNearestIsoObject(int x, int y, int z, bool fixZ = true, isoType = 'IsoThumpable'): IsoObject|false

in my framworks util class? Since I'm pretty sure, I will require such an function as well sooner or later for my current project.

 

I can share the the code here. I get IsoChunks around the player you can just adjust the values if you want it bigger.

local function getField(class, fieldName)
    local i = 0
    while true do
        local field = getClassField(class,i)
        if field:getName() == fieldName then
            return getClassFieldVal(class,field)
        end
        i = i + 1
    end
end
 
local function getVehiclesAroundPlayer()
    local currentCell = getWorld():getCell() --works
    local player = getPlayer()
 
    local halfSize = 20 --Increase by 10 to get more chunks
    local varX = player:getX() - halfSize
    local varY = player:getY() - halfSize
    local yOrigin = varY
 
    local incr = 10
    local fullSize = halfSize * 2
    local allVehicles = {}
    for _ = 0, fullSize, incr do
        for _ = 0, fullSize, incr do
            local chunk = currentCell:getChunkForGridSquare(varX, varY, 0)
            if not chunk then
                return
            end
            print(chunk)
            local baseVehicles = getField(chunk,"vehicles")
            for i = 0, baseVehicles:size()-1 do
                local vehicle = baseVehicles:get(i)
                local alreadyFound = false
                for _, car in ipairs(allVehicles) do
                    if car:getId() == vehicle:getId() then
                        alreadyFound = true
                        break
                    end
                end
                if alreadyFound == false then
                    table.insert(allVehicles,vehicle)
                end
                print("vehicle Id: ",getField(vehicle,"VehicleID"))
            end
            varY = varY + incr
        end
        varY = yOrigin
        varX = varX + incr
    end
    return allVehicles
end
getVehiclesAroundPlayer()
Edited by Kenneth
Link to comment
Share on other sites

You can get the Field instances and use their methods in debug mode.

You can get the Field instances but not use their methods in non-debug mode.

 

In order to make it work out of debug mode, you can use (at your own risks) the string casts of those Field instances.

If you go this road, I strongly suggest to do this parsing only once per fieldName, on first request or during init.

 

local Verbose = true
function getField(class, fieldName)
    local i = 0
    while true do --TODO for loop with getNumClassFields(Object var0)
        local field = getClassField(class,i)
        if not field then
            if Verbose then print ("param "..i.." "..tostring("no field")); end
            return nil
        end
        
        --private field is not accessible out of debug mod and any access to it (even modifiers reading wil crash)
        --that's why I use the dirty string analysis below.
        local stringField = tostring(field)
        local isPrivate = string.sub(stringField,1,string.len("private"))=="private"
        local isProtected = string.sub(stringField,1,string.len("protected"))=="protected"
        local isRightName = fieldName == "" or stringField:sub(-#fieldName) == fieldName
        
        if not isPrivate and not isProtected then
            if ShoesSpeed.Verbose then print ("param "..i.." is public."); end
            if isRightName then
                return field
            end
            --if we reach here, it was not the right field (but public)
        else
            --if we reach here, it was not the right field (and private or protected)
            if Verbose then print ("param "..i.." is private or protected."); end
        end
        
        i = i + 1
    end
    return nil
end

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...