Run Tarantool Lua function in another coroutine - lua

I use NoSQL database Tarantool and try to do some complex work on DB side using Lua stored procedures. I think it`s a good idea, because i can do less DB calling and have less overhead with network data transfer.
I have some table:
user_counters: id, counter_a, counter_b, score
And, for example, i have some function to calculate field score:
function recalc_score(id)
local stream = box.space.user_counters:select { id }
local rating = 0
-- some_rating_calculation using counter_a and counter_b here
box.space.user_counters:update(id, { { '=', 4, rating } })
end
And i have another function for fields counter_a and counter_b update:
function update_user_counters(id, counter_a_diff, counter_b_diff)
local rating_default = 0
local user_counters_tuple = box.space.user_counters:upsert(
{ id, counter_a_diff, counter_b_diff, rating_default },
{ { '+', 2, counter_a_diff }, { '+', 3, counter_b_diff } }
)
-- start another coroutine recalc_score(id) and forget about it
return user_counters_tuple
end
How can i call recalc_score(id) function and return user_counters_tuple without waiting when previous function execution will be finished?

Just use fiber.create(fun, ...):
local fiber = require('fiber')
-- start another coroutine recalc_score(id) and forget about it
fiber.create(recalc_score, id)

Related

Can't find a way to save a table of data

I have been working on a survival game base on "Booga Booga" but i cant seem to find out how to load player data on the game. The data im trying to load is saved in the for loop that follows:
module.SaveData = function (player, DT)
local data_saved = {}
local setData = player.inventory.Inv:GetChildren()
for i, v in pairs(setData) do
table.insert(data_saved, {[v] = {
value = v.Value,
name = v.Name
}})
end
Data_Store:SetAsync(player.UserId, data_saved)
end
I've done multiple things to attempt to solve this problem
I've tried load with http service
I've already attempted loading the raw table
and I've tried to use a global data store instead
here is my code that loads the data as of now:
game.Players.PlayerAdded:Connect(function(plr)
CF.PlayerInventorySetup(plr) -- not relavent
p = plr -- not relavent
local PD =require(game.ServerScriptService.DataHandler)
local plrdata =PD.FetchData(plr)
for i, v in pairs(plrdata) do -- this is what im having trouble with
if not plr.inventory.Inv:FindFirstChild(v) then
local newint = Instance.new("NumberValue")
newint.Name = v.name -- the ouput says: string expected, got nil
newint.Value = v.value --
newint.Parent = plr.inventory.Inv
end
end
end)
I actually don't know wtf to do.
Before we talk about a solution, let's talk about what's happening when you save the data. Let's say for example, your inventory is a list of NumberValues like this :
Fish (value of 3)
Iron (value of 10)
Grass (value of 8)
Your FetchData function expects that the saved player data is formatted like this :
{
{ value = 3, name = "Fish" },
{ value = 10, name = "Iron" },
{ value = 8, name = "Grass" },
}
However, the result of the loop in the SaveData function would be this :
{
{ Fish(NumberValue) = { value=3, name="Fish" }},
{ Iron(NumberValue) = { value=10, name="Iron" }},
{ Grass(NumberValue) = { value=8, name="Grass" }},
}
On every step of the loop, you are pushing a new dictionary into the data_saved table.
The FetchData function expects that dictionary to have keys for "name" and "value". But, you've pushed that data one layer deeper, so those keys don't exist where the FetchData code expects them to and it throws errors.
So to fix your issue in the SetData function, remove the outer layer of that table, and just use the raw data.
table.insert(data_saved, {
value = v.Value,
name = v.Name
})

How to pass a table key in a function for use in a for loop?

For some reason it doesn't appear to work to pass in a table key as a function argument, what is the trick to do this?
I'm trying to wrap the for loop iteration technique in vanilla Lua into a function that has three arguments: (1) the table to iterate, (2) the table_key to check each time, and (3) the value to find. If a match is found, return it, otherwise return nil.
function table_find_match(table, table_key, match_value)
for i=1, #table do
local this = table[i]
if this[table_key] == match_value then
return this[table_key]
end
end
return nil
end
local table_example = {
{
key_example = "string_value_1"
},
{
key_example = "string_value_2"
}
}
local result = table_find_match(table_example, key_example, "string_value_1")
print(result)
Found a solution, if I pass in the table key as a string it works, such as
table_find_match(table_example, "key_example", "string_value_1")
but I really dislike having to convert it into a string, if anyone knows any other workaround to this please share
If you pass it like table_find_match(table_example, key_example, "string_value_1")
the key_example is now considered as a (nil) variable if not defined before executing, so it has to be like
local key_example = "key_example"
local result = table_find_match(table_example, key_example, "string_value_1")
print(result)

store lua functions in queue to be executed later

I am using functions from a lua-based library and I would like to store those different functions with varying parameter counts in a queue to be executed later on when I need to. Here is a similar question and solution for javascript.
Below is an example that works, but conditionals are done manually for each parameter count. Is there a cleaner way to do this in lua? Keep in mind I cannot modify the library's function implementation since I dont have access to their code.
Action = {}
function Action:new(func, ...)
newObj = {
func = func or nil,
args = {...} or {}
}
self.__index = self
return setmetatable(newObj, self)
end
function Action:execute()
if #self.args == 0 then
self.func()
elseif #self.args == 1 then
self.func(self.args[1])
elseif #self.args == 2 then
self.func(self.args[1], self.args[2])
-- and so on
end
theQueue = {
Action:new(aFunc, param),
Action:new(aDifferentFunc, param1, param2)
}
for _, action in ipairs(theQueue) do
action:execute()
end
You can simply use table.unpack to convert your args table back to a parameter list.
self.func(table.unpack(self.args))
newObj should be local btw.
This looks like a perfect case for anonymous functions:
local theQueue = {
function() aFunc(param) end,
function() aDifferentFunc(param1, param2) end,
}
for _, action in ipairs(theQueue) do
action()
end

Avoid conflicting when "matching" users for multiplayer game

I have tried both FireBase and PubNub to create this simple multiplayer game. Created with only two players. One big(and justified) concern is conflicting users. Let me explain :
each "game" constructed with just two players(not more). If 4 players logs as the same time. And each player search for a "match". player one might match with player two.while player two might match with player three, and so on.
How can i avoid it, and guarantee that each player will get a single and unique match?, or in other words , prevent matching one user with more than other one
With Firebase, security rules and transactions would be the key to an elegant solution.
If you're willing to set up a node.js script or other server-side worker, this is fairly straightforward. Players would write to a "lobby" when they want a match. The server script would perform the matches and write back the "game room" they are going to join. The structure would be basically thus:
/games/$game_id/users/user1/<user_id>
/games/$game_id/users/user2/<user_id>
/lobby/$user_id/false (an unmatched user)
/lobby/$user_id/$game_id (a matched user)
Now clients would simply write to the lobby when they want to join a game, and then wait for the server to assign them a game id:
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
var lobbyRef = ref.child('lobby/' + <my user id>);
lobbyRef.set(false); // I want to join a game
lobbyRef.on('value', function(snap) {
if( snap.val() !== null ) {
console.log('I joined a game!', snap.val());
}
});
The server is nearly as simple. Assuming node.js:
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
var lobbyRef = ref.child('lobby');
var gamesRef = ref.child('games');
var player1 = null;
// listen for requests to join
lobbyRef.on('child_added', function(snap) {
// assign as player1 if nobody is waiting
if( !player1 ) {
player1 = snap.ref();
}
// if someone is already waiting, assign both players a room
else {
var player2 = snap.ref();
var gameRef = gamesRef.push({
players: {
player1: player1.key(),
player2: snap.key()
}
}, function(err) {
if( err ) { throw err; } // a bug
// let the players know they have been matched and which room to join
player1.set(gameRef.key());
player2.set(gameRef.key());
});
}
});
Obviously there is some work to make all this fault tolerant and security rules would be needed to prevent cheating.
Doing this entirely on the client is slightly more involved, but certainly manageable.
Have each player attempt to match themselves to anybody in the lobby. If nobody is in the lobby, then wait there. This is done with a transaction to prevent conflicts.
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
var lobbyRef = ref.child('lobby');
function waitInLobby() {
lobbyRef.once('value', lobbyUpdated);
}
function lobbyUpdated(snap) {
if( snap.val() === null ) {
// lobby is empty, so join and wait
var ref = lobbyRef.child('<my user id>').push(false);
ref.on('value', someoneMatchedMe);
function someoneMatchedMe(snap) {
if( snap.val() !== false ) {
console.log('I got matched in room', snap.val());
ref.off('value', someoneMatchedMe); // stop monitoring
snap.ref().remove(); // leave the lobby
}
}
}
else {
// lobby is not empty, so try to match someone
var possibles = [];
snap.forEach(function(ss) {
possibles.push(ss.ref());
});
}
}
function matchUser(possibles) {
if( !possibles.length ) { waitInLobby(); }
var opponentRef = lobbyRef.child(possibles.shift());
opponentRef.transaction(function(currentVal) {
if( currentVal !== false ) {
// already claimed, start over
matchUser(possibles);
}
});
}
Some security rules would be critical here, in addition to the transactions. There is also plenty of room for optimization, but at the point that you're optimizing for production, that's a job for your engineering team, rather than a Stack Overflow contributor.
matching
[p1] - [p2] - [p3] - [p4] - [p5] - etc...
Ok so you match odd numbered player (N) with the next even numbered player (N + 1).
Of course P5 stays alone and should wait for the next round, make him P1 for that round. That way he never has to wait 2 rounds
You can create a tuple for the pairs, but I would make the Player class also have a field oponent of type Player
edit1: You keep track of the raw queue in a regular array of Players. As soon as the array reaches it's desired size you trigger the above algorithm which stops the ability to change to current player pool and all matches will be definitive.
idle check
Ok so you let players enter the queue and you display a timer / empty slot counter so they have feedback how long they have to wait.
As soon as the match starts you let them lock in (League of Legends does it this way as well)
If 1 or more players do not lock in you start the queue process over, maybe with a decreased timer so the players don't have to wait to long.
If you make this time based (not slot based) then if 1 players does not respond (let's say P2) you move the last player (P5) to his slot (P5 is now P2) and everyone can play.
If you have more questions I will edit this answer.

How to save boolean conditions and evaluate later

I need to create a structure. The structure must contain an array of "boolean conditions". Something like this:
function ReturnStructure ()
local structure = {
{A < 10},
{B == "smth"},
{FunctionReturnsTrueOrFalse(params)},
--...
}
return structure
end
structure = ReturnStructure()
print(structure[1][1]) -- prints true or false depending on the value of A
In fact these tables contain true or false, not conditions, because when we call function ReturnStructure and it creates a local table structure, all conditions in the fields will be executed. I want to create a structure whose fields will contain not boolean values, but something that I can execute (when I want to do it) and get a boolean value. I can achieve this by using anonymous functions:
function ReturnStructure ()
local structure = {
{function() return A < 10 end},
{function() return B == "smth" end},
{FunctionReturnsTrueOrFalse, params}, -- I don't call function in this line, a just put its adress and parameters to table.
--...
}
return structure
end
structure = ReturnStructure()
print(structure[1][1]) -- prints function: 0x109bdd0
print(structure[1][1]()) -- prints true or false. I execute condition in this string.
So, there is a code which works as I want it to, but it seems very ugly.
I want to hear some ideas on how to create a simpler and more beautiful table, without printing function () return ... in every field. I think that I should use a simple OOP implementation to create my structure as an object, but I don't know how to do it. I also will be happy to get some references to methods, implementations, articles etc., which can help me to find some ideas.
I want to hear some ideas on how to create a simpler and more beautiful table, without printing function () return ... in every field.
There aren't. If Lua had C#'s lambda syntax, you could write:
local structure = {
() => A < 10,
() => B == "smth",
() => FunctionReturnsTrueOrFalse(params),
But Lua likes to keep things small and simple, to avoid adding size and complexity to the language and its implementation, so we have one syntax for one function type.
You could store them as strings, then compile and run them later, but that's choosing form over function. You don't want to be invoking the compiler unnecessarily.
local structure = {
'A < 10',
'B == "smth"',
'FunctionReturnsTrueOrFalse(params)',
So your original solution is better. I don't particularly like the way that the first two items defer evaluating their arguments, while your third example evaluates the parameters at compile time. It would be more consistent to treat the FunctionReturnsTrueOrFalse the same
local structure = {
function() return A < 10 end,
function() return B == "smth" end,
function() return FunctionReturnsTrueOrFalse(param1) end,
This also means you don't need to put them in tables. Each is just a function you call, which simplifies the calling code as well.
If you really wanted to evaluate FunctionReturnsTrueOrFalse's arguments at compile time, I'd write a utility routine to build a closure from a function and its arguments:
local function bind(f, ...)
local args = {...}
return function() f(unpack(args)) end
end
Then use that to bind the function to its args:
local structure = {
function() return A < 10 end,
function() return B == "smth" end,
bind(FunctionReturnsTrueOrFalse, param1, param2, param3),
Then everything in your table is simply a function so you don't need special handling
function ReturnStructure ()
local structure = {
{'A < 10'},
{'B == "smth"'},
{FunctionReturnsTrueOrFalse, 'parameter'},
}
local function call_me(f, ...)
return (type(f)=='function' and f or
assert((load or loadstring)('return '..f)))(...)
end
return setmetatable({}, {
__index =
function(t,k)
if structure[k] then
return call_me((table.unpack or unpack)(structure[k]))
end
end,
__newindex = function(t,k,v) structure[k] = v end
})
end
A = 2
B = "anything"
function FunctionReturnsTrueOrFalse(par)
return #par > 5
end
structure = ReturnStructure()
print(structure[1]) -- true
print(structure[2]) -- false
print(structure[3]) -- true
structure[4] = {'1==0'}
print(structure[4]) -- false

Resources