Jump to content

Call a table function from another resource


Dzsozi (h03)

Recommended Posts

Hello!

I would like to make a mission manager resource. Let's say I have a resource called missionmanager. In this resource I have script, that looks something like this:

availableMissions = {}
function startMission(id)
  if id and availableMissions[id] then
    if availableMissions[id].startFunction then
      availableMissions[id].startFunction()
    end
  end
end

And then I have a different script in this same resource with the mission like this:

availableMissions[1] = {}
availableMissions[1].startFunction = function()
  outputChatBox("a")
end

I would like to use the startMission function as an export function in other different resources, so I can start a mission whenever I want with the startMission(1) function.

I've tested the above codes but it doesn't work, only if I put them in the same script, but I don't want to make every mission in one script, I would like to have different script files for each mission.

How can I do that?

Link to comment
  • Moderators

Your current problem is that you can't manipulate the same table in two different resources, right?

------------------------------- | -------------------------------

                                   |

   Environment 1      |   Environment 2

                                   |

------------------------------- | -------------------------------

This is indeed a very annoying problem.

 

But lets get back to the possibilities we have.

<export /> This exports functions from this resource, so other resources can use them with call

function: The function name

type Whether function is exported server-side or client-side (valid values are: "client", "server" and "shared")

http: Can the function be called via HTTP (true/false)

https://wiki.multitheftauto.com/wiki/Meta.xml

 

UNTESTED

Example below is untested, but it will probably do something similar to what you want. Write a good wiring example.

 

availableMissions = {}
function startMission(resourceName, ...)
	if resourceName and availableMissions[resourceName] then
		if availableMissions[resourceName].communicator then
			call ( getResourceFromName (resourceName), availableMissions[resourceName].communicator, ...)
		end
	end
end

function addMission (missionData)
  	local resourceName = getResourceName(sourceResource) 
  	-- https://forum.multitheftauto.com/topic/33407-list-of-predefined-variables/
  	-- https://wiki.multitheftauto.com/wiki/Call
  
	availableMissions[resourceName] = missionData
  	--availableMissions[missionData.resourceName] = missionData
end

 

local mission = {}

function communicator (functionName, ...)
	mission[functionName](...)
end

mission.startFunction = function(text)
  outputChatBox(text)
end


call ( getResourceFromName ("missionManager"), "addMission", {communicator = "communicator", resourceName = "example"} )


call ( getResourceFromName ("missionManager"), "startMission", "startFunction", "Hi!" )

 

 


UNTESTED

You can also build an example which can be dynamic upgrade-able.

local resourceConnections = {}

function connect (resourceData)
	local resourceName = getResourceName(sourceResource) 
	resourceConnections[resourceName] = resourceData
	if resourceData.communicationFunctions then
		call ( getResourceFromName (resourceName), availableMissions[resourceName].communicator, "getCommunicationFunctions", 'function reConnect() call( getResourceFromName ("missionManager"), "reConnect") end')
	end
	return true
end

function reConnect (text)
	outputChatBox(text)
	-- ...
end

 

local resourceFunctions = {}

function communicator (functionName, ...)
	resourceFunctions[functionName](...)
end

function resourceFunctions.getCommunicationFunctions (communicationFunctions)
	loadstring(communicationFunctions)()
end

local connected = call( getResourceFromName ("missionManager"), "connect", {communicator = "communicator", communicationFunctions = true})

iprint(connected)

reConnect("hmm does this work???")

 

Damn it is brain fu... while writing this example without any testing.

 

 

Edited by IIYAMA
  • Like 1
Link to comment

Hmm, it is kinda strange, I don't get any errors but neither any outputs when using your first code. If I understood correctly, I must use the first code in the missionmanager resource and the second code of the first example in the any other resource I would like to. But I get an error when trying to use your second example's codes: missionmanager/mission1.lua: failed to call 'missionmanager:connect' and the iprint returns a false value.

Can you help me out? I don't know what could be the problem. Also I would like to ask you to explain a few things for me please. I have never used the loadstring function, what does that do and how to use it? And I don't know what's the difference between the ' and " in lua. Why the whole function is between ' ? Or this is what loadstring is good for? I don't quite understand it.

Link to comment
  • Moderators

Small fix, line 7: (forgot to pass the text through the reConnected function (in the string).

call ( getResourceFromName (resourceName), availableMissions[resourceName].communicator, "getCommunicationFunctions", 'function reConnect(...) call( getResourceFromName ("missionManager"), "reConnect", ...) end')

 

 


"" = string (woman)

'' = string (man)

Nah, they are the same. There are two types in case you want to use string characters inside of the string.

 

So:

"''" -- the string contains these characters: ''
'""' -- the string contains these characters: ""

 

In the code I posted, I didn't past a function through it. BUT I past un-executed code (as text) from RESOURCE A to RESOURCE B.

 

This is what loadstring does:

a = 1

local functionForLoadedCode = loadstring("a = a + 1") -- load the string before execute it. And return a function which will run the code.

functionForLoadedCode() -- The code has been executed!

print(a) -- 2


functionForLoadedCode() -- The code has been executed!

print(a) -- 3

for i=1, 1000 do
    functionForLoadedCode() -- The code has been executed! X 1000! Woohoo!
end

print(a) -- 1003

https://www.lua.org/pil/8.html

 


 

Quote

But I get an error when trying to use your second example's codes: missionmanager/mission1.lua: failed to call 'missionmanager:connect' and the iprint returns a false value.

Don't know, might be caused by the timing.(as resources can't be called before they are loaded)

Better use the "onResourceStart" event before the calling. And don't forget to make the function call able within the meta.xml.

 

@Dzsozi (h03)

 

Link to comment

Thank you for your explanations! Unfortunately, I am still getting an error message, this time I tried using it with a command.

I have also made the connect and reConnect functions callable within the missionmanager resource. I put the

local resourceFunctions = {}

function communicator (functionName, ...)
	resourceFunctions[functionName](...)
end

function resourceFunctions.getCommunicationFunctions (communicationFunctions)
	loadstring(communicationFunctions)()
end

function testme()
  local connected = call( getResourceFromName ("missionManager"), "connect", {communicator = "communicator", communicationFunctions = true})

  iprint(connected)

  reConnect("hmm does this work???")
end
addCommandHandler("tme", testme)

code in a different resource.

But when I use the tme command I get the following errors, even though I changed line 7 to your new line:

missionmanager\server.lua:7: attempt to index field '?' (a nil value)
testmission\sourceS.lua:12: call: failed to call 'missionmanager:connect'
INFO: false (this is the iprint)
testmission\sourceS.lua:16: attempt to call global 'reConnect' (a nil value)

Edited by Dzsozi (h03)
Link to comment
  • Moderators
Quote

missionmanager\server.lua:7: attempt to index field '?' (a nil value)

The /\ availableMissions /\ variable is from your previous code make sure you check and clean that for yourself too.

 

call ( getResourceFromName (resourceName),  resourceConnections[resourceName].communicator, "getCommunicationFunctions", 'function reConnect() call( getResourceFromName ("missionManager"), "reConnect") end')

 

 


Quote

testmission\sourceS.lua:12: call: failed to call 'missionmanager:connect'

If the problem from above doesn't solve the this problem:

 

Make sure to debug everything:

iprint(getResourceFromName ("missionManager"))

Can it find the resource?

<script src="connect.lua" type="server"/>

Is the export line applied to the target resource?

 

 


 

Quote

testmission\sourceS.lua:16: attempt to call global 'reConnect' (a nil value)

This will not work because of the problem from above.

 

@Dzsozi (h03)

 

Link to comment
  • 4 weeks later...

Sorry for not replying, I did some other things but now I returned to this resource. I don't really understand the way your script would work, I started making something else. I managed to do a kind of simple mission manager. I have a resource called sa_missionmanager where I handle mission starting and stopping etc. and a resource called sa_missions where I store the missions and data of them in seperate folders that's named after the mission ID.

Here's my current sa_missionmanager server side script:

addEvent("onPlayerMissionStart", true)
addEvent("onPlayerMissionStop", true)

function setPlayerMission(player, missionID)
	if exports["sa_utility"]:isPlayer(player) then
		if type(missionID) == "number" then
			local mission, id = getPlayerMission(player)
			if missionID ~= 0 then
				if id ~= missionID and not isElement(mission) then
					local missionElement = createElement("mission", missionID)
					setElementParent(player, missionElement)
					
					local team = getPlayerTeam(player)
					local teamName = ""
					if team then
						teamName = getTeamName(team)
					end
					
					triggerEvent("onPlayerMissionStart", player, missionID, teamName)
					
					return missionElement, missionID
				end
			else
				triggerEvent("onPlayerMissionStop", player, id)
				
				if isElement(mission) then
					destroyElement(mission)
					mission = nil
				end
				
				return nil, 0
			end
		end
	end
end

-- THIS IS WHERE I HAVE MY PROBLEMS
addEventHandler("onPlayerMissionStart", root,
	function(id, team)
		local currentMissionData = exports["sa_missions"]:sendMissionData(id)
		outputChatBox(tostring(currentMissionData[1]["Criminal"](source))) -- returns nil???
    	outputChatBox(tostring(currentMissionData[1]["name"])) -- returns 'Hunnid on your wrist'
    
		if currentMissionData[id] then
			if currentMissionData[id][team] then
				currentMissionData[id][team](source) -- this should start the mission depending on the player's team
			end
		end
	end
)

-- I KNOW, THERE IS NO currentMissionData DEFINED BUT IT'S NVM RIGHT NOW
addEventHandler("onPlayerMissionStop", root,
	function(id)
		if currentMissionData[id] then
			outputChatBox("end")
		end
	end
)

function getPlayerMission(player)
	if exports["sa_utility"]:isPlayer(player) then
		for _, mission in ipairs(getElementsByType("mission")) do
			local children = getElementChildren(mission, "player")
			for _, child in pairs(children) do
				if child == player then
					return mission, getElementID(mission)
				end
			end
		end
	end
	return nil, 0
end

function testfunction(player, command, id)
	if id then
		setPlayerMission(player, tonumber(id))
	else
		outputChatBox("/" .. command .. " [id]", player)
	end
end
addCommandHandler("mission", testfunction)

My current sa_missions server side script:

function sendMissionData(id)
	if id and type(id) == "number" then
		return missionData
	end
end

My current sa_missions global/shared script:

missionData = {}

My current sa_missions/missions/1/data global/shared script:

missionID = 1
missionData[missionID] = {
	["name"] = "Hunnid on your wrist",
	["maxLevel"] = 5,
	["reward"] = {money = 100, xp = 50},
}

My current sa_missions/missions/1 server side script:

missionData[missionID]["Criminal"] = function(player)
	return getPlayerName(player) -- I added this because of the outputchatbox in the missionmanager
end

I know it is lots of code, I tried to delete the unwanted codes that work and focus on the structure mainly of the scripts. So I don't understand what is wrong with this script. Why does the function not get added to the missionData[missionID] table? Everything has the correct value when talking about outputting it in the missionmanager resource, but the function, it returns nil in the chat and nothing happens.

Debugscript says: attempt to call field 'Criminal' (a nil value)

What is the issue, what am I doing wrong? I would like to make my missions system based on something similar structure because I want to have lots of missions created and I would like them so it can be handled easily without writing the same lines of code in every mission script. So basically I only have to do the mission scripting itself, that's why I would like to have a mission manager.

EDIT: Could the problem be that I am trying to add a value on a server side script to the table created in a global script?

@IIYAMA

Edited by Dzsozi (h03)
Link to comment
  • Moderators

Nope, your current problems:

- Exported content is always a copy. So if you were to pass a table from one resource to another, then the content might be the same, but it is not the same table.

 

To get a better understanding of this:

1. Create two resources.(if you haven't done that already)

2. Create in one resource a table.

3. Debug the table like this:

theTable = {}
print(tostring(theTable))

 

4. Export the table to the 2e resource.

5. Repeat step [3] in the 2e resource.

 

It will tell you that both tables do have a different userdata. That means they are not the same.

 

________________

- You can't pass functions to another resource. (Except as a string as I did in my example)

And if you did it as a string, it would be a copy of it.

The reason why you can't do this is because a function has a function block which only exist at the place where it is created.

 

Which comes to this conclusion:

The environment of both resources can't be merged.

Which would be merged if:

- Tables can be shared between resources.

- Functions can be accessed without exporting.

 

@Dzsozi (h03)

 

 

Edited by IIYAMA
Link to comment

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
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...