1

I want to avoid that a player can call server.UpdateUserInternalData very frequently. Therefore I want to save the timestamp of the last server.UpdateUserInternalData call in UserInternalData.

I just want to update "PlayerData1": newdata if the last server.UpdateUserInternalData call was at least 4000 milliseconds before the current time(currenttimeinmilliseconds). But I don't know what I should do if the last update call was not at least 4000 milliseconds before the current time(currenttimeinmilliseconds).

Is it possible to wait in Function1 until ((Number(playertimestamp) + 4000) < Number(currenttimeinmilliseconds)) is true and then continue normally with updating "PlayerData1": newdata ? I don't want that the line return issuccessful; is executed before "PlayerData1": newdata is executed.

How can I do that? How can I wait in Function1?

function Function1(string newdata)
{
  var issuccessful = "";
  var playertimestamp = GetTimestampInternalData();
  var currenttimeinmilliseconds = getServerTimestamp();

  if ((Number(playertimestamp) + 4000) < Number(currenttimeinmilliseconds))
  {
    server.UpdateUserInternalData({
       PlayFabId: currentPlayerId,
       Data: {
           "PlayerData1": newdata
       },
       Permission: UserDataPermission.Private
    });

    issuccessful = true;

    var timestampinmilliseconds = getServerTimestamp();      
    CreateTimestampInternalData(timestampinmilliseconds);
  }

  return issuccessful;
}

function GetTimestampInternalData()
{
  var resultdata = server.GetUserInternalData({PlayFabId: currentPlayerId, Keys: "InternalDataTimestamp"});
  var currenttimestamp = "";
  if ((resultdata.Data.hasOwnProperty("InternalDataTimestamp")) && (resultdata.Data.InternalDataTimestamp.Value != null) && (resultdata.Data.InternalDataTimestamp.Value != ""))
    currenttimestamp = resultdata.Data.InternalDataTimestamp.Value;
  else
    currenttimestamp = 0;

  return currenttimestamp;
}

function CreateTimestampInternalData(currenttime)
{
  server.UpdateUserInternalData({
       PlayFabId: currentPlayerId,
       Data: {
           "InternalDataTimestamp": currenttime.toString()
       },
       Permission: UserDataPermission.Private
    });   
}

function getServerTimestamp()
{
  var now = new Date();
  var time = now.getTime();
  return time;
}

EDIT: The function sleep(milliseconds) in this article(Bringing Sleep to Native JavaScript) works for me if I just want to wait a few seconds before the next line of code is executed: https://www.sitepoint.com/delay-sleep-pause-wait/

Hobbit7
  • 313
  • 3
  • 16
  • You can use setTimeout() to wait a specified period of time before you execute some code. see https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout – ADyson Jan 04 '20 at 09:48
  • 1
    Does this answer your question? [Wait 5 seconds before executing next line](https://stackoverflow.com/questions/14226803/wait-5-seconds-before-executing-next-line) – Eldar Jan 04 '20 at 10:07
  • As others suggest you can use `setTimeout` or similar, but note that it probably implies that you'll have to rewrite many stuff in an asynchronous way, with callbacks / promises / async-await in order to get the results of your functions. In javascript there's no synchronous "sleep" functions as you can find in some other programming languages – Joel Jan 04 '20 at 10:09
  • In game programming, a common approach is to have a "game loop" as the backbone of the whole application, where updating functions of every game elements are called at regular intervals. That can be simply setup in javascript with `setInterval`, but then your whole program architecture would have to be articulated around it. – Joel Jan 04 '20 at 10:17

5 Answers5

2

You can use a debounce function from lodash for example.

A debounce function delays invoking your function until after X milliseconds have elapsed since the last time the debounced function was invoked.

usage :

_.debounce(Function1, 4000);

This will run your function only if 4000 ms have elapsed since the last time.

Morta1
  • 609
  • 7
  • 20
  • +1 for debounce. `setTimeout` that others have suggested won’t have the desired effect. It’s just deferring something that can/will still happen multiple times in a 4 second period. – Mike Jan 04 '20 at 11:27
1

use setTimeout() in your code it will works

setTimeout(function(){ alert("Your function here"); }, 4000); //4000 is 4 seconds

For Eg:

function onLoadFunction() {
  setTimeout(function(){ 
     yourfunction();
 }, 4000);
}
Udhay Titus
  • 5,761
  • 4
  • 23
  • 41
0

You can use setTimeout() in your Function1() in order to execute it after 4 seconds

For Example:

 function Function1(string newdata){
   var issuccessful = "";
   var playertimestamp = GetTimestampInternalData();
   var currenttimeinmilliseconds = getServerTimestamp();

  setTimeout(function(){ 
   server.UpdateUserInternalData({
   PlayFabId: currentPlayerId,
   Data: {
       "PlayerData1": newdata
   },
   Permission: UserDataPermission.Private
});

issuccessful = true;

var timestampinmilliseconds = getServerTimestamp();      
CreateTimestampInternalData(timestampinmilliseconds);
   }, 4000);

  }

There is no need to compare your previous timestamp with current timestamp for 4 secs, once your Function1() is called setTimeout() will get triggered after a given time(which is 4 secs in this case).

Arpit Bhatia
  • 173
  • 1
  • 8
  • I want that there are at least 4 seconds between two server.UpdateUserInternalData calls. But on the other hand, I don't want to wait 4 seconds if the last server.UpdateUserInternalData call was already long time ago, for example 30 seconds. In this case, server.UpdateUserInternalData can directly be executed because the last server.UpdateUserInternalData call is already older than 4 seconds, it doesn't need to wait 30s + 4s = 34s. But in your example, the code will always wait 4 seconds even if the last server.UpdateUserInternalData call is already older than 4 seconds, or am I wrong? – Hobbit7 Jan 04 '20 at 10:33
  • Alright understood, I think you can resolve the problem by using Promise or Async-await – Arpit Bhatia Jan 04 '20 at 12:17
0

You can implement a debounce function that ensures your function1 is not called more frequently than once every 4000 milliseconds using the following function:

let scheduled = null;
let fist_bounce = true;
/* causes function1 to be executed only once every ms milliseconds */
function debounce(newdate, ms)
{
    if (first_bounce) { // don't wait for first call
        first_bounce = false;
        function1(newdate);
    }
    else if (!scheduled) {
        scheduled = setTimeout(() => {
            function1(newdate);
            scheduled = null;
        }, ms);
    }
}

Demo:

function function1(newdate)
{
    console.log('function1 called: ' + newdate);
}

let scheduled = null;
let first_debounce = true;
/* causes function1 to be executed only once every ms milliseconds */
function debounce(newdate, ms)
{
    if (first_debounce) { // don't wait the first time
        first_debounce = false;
        function1(-1);
    }
    else if (!scheduled) {
        scheduled = setTimeout(() => {
            function1(newdate);
            scheduled = null;
        }, ms);
    }
}

/* test debounce: */

let i = 0;
let interval = setInterval(() => {
    ++i;
    debounce(i, 2000); // execute function1 once every two seconds
    if (i == 40) {
        clearInterval(interval);
    }
}, 200); // call debounce every 200ms

To make a multiple "debouncers" usable for multiple functions, we rely on a closure:

function make_debouncer(fn, ms)
{
    let first_debounce = true;
    let scheduled = null;
    function debounce()
    {
        let args = Array.prototype.slice.call(arguments);
        if (first_debounce) {  // don't wait for first call
            first_debounce = false;
            fn.apply(null, args);
        }
        else if (!scheduled) {
            scheduled = this.setTimeout(function() {
                fn.apply(null, args);
                scheduled = null;
            }, ms);
        }
    }

    return debounce;
}


/* test debounce: */

function Function1(newdate) {
    console.log(newdate);
}
    
// let debounce = make_debouncer(Function1, 2000); // call once every 2 seconds
// better yet, let's redefine Function1:
Function1 = make_debouncer(Function1, 2000); // call once every 2 seconds

let i = 0;
let interval = setInterval(function() {
    ++i;
    Function1(i); // call Function1 passing i as an argument
    if (i == 40) {
        clearInterval(interval);
    }
}, 200); // call debounce every 200ms
Booboo
  • 38,656
  • 3
  • 37
  • 60
  • What is the difference between your answer and the answer that Arpit Bhatia posted? Is your code not doing the same thing? – Hobbit7 Jan 04 '20 at 15:01
  • It appears that if you call his (her?) `Function1` twice in rapid succession then `server.UpdateUserInternalData` will be called twice in rapid succession, albeit not for four seconds for each call. But that's not what you want. My demo calls `debounce` 40 times separated by 200ms for each call yet `Function1` is only called once at most every 2 seconds. `debounce` also abstracts out all the timing and a slight change can make it a nice reusable general purpose function. – Booboo Jan 04 '20 at 15:27
  • Is it possible to make something like this? function foo(string text) { console.log(text); } Is it necessary that I make some changes on the rest of the code if I want to use string text? – Hobbit7 Jan 04 '20 at 16:25
  • You can do: `let debounce = make_debouncer(() => foo('abc'), 1000);` But if the argument changes, then I have to update the answer. – Booboo Jan 04 '20 at 16:32
  • The argument is not changing. It's always "abc". But the code is never working, I always get an error message in this line: let debounce1 = make_debounce(foo, 4000); "ReferenceError: make_debounce is not defined\n at handlers.UpdatePlayerData (BFD0A-main.js:1144:20)\n at Object.invokeFunction (Script:116:33)" – Hobbit7 Jan 04 '20 at 16:39
  • It's `make_debouncer`, sorry. And I have updated the all the code to handle arguments to `Function1` or whatever. – Booboo Jan 04 '20 at 16:51
  • One more slight, inconsequential change. – Booboo Jan 04 '20 at 18:14
  • I get an error message in this line. What am I doing wrong? "ReferenceError: setInterval is not defined\n at handlers.UpdatePlayerData (BFD0A-main.js:1153:18)\n at Object.invokeFunction (Script:116:33)" let interval = setInterval(() => { ++i; Function1(i); if (i == 40) { clearInterval(interval); } }, 200); – Hobbit7 Jan 04 '20 at 19:06
  • On the client side, you have to say `window.setInterval` and `window.setTimeout` (in function `makeDebouncer`). – Booboo Jan 04 '20 at 19:13
  • I modified `make_debouncer` so that it should run in older browsers. But you would have to test this. – Booboo Jan 04 '20 at 19:30
  • It's not working: "ReferenceError: window is not defined\n at handlers.UpdatePlayerData (BFD0A-main.js:1153:18)\n at Object.invokeFunction (Script:116:33)" – Hobbit7 Jan 04 '20 at 19:35
  • Are you running this in a browser? If so, you need to post your code in your question. – Booboo Jan 04 '20 at 19:54
  • I changed `window` to `this`, which is either `window` or `global` depending on the environment. It should work on the client or in `node js`. – Booboo Jan 04 '20 at 20:02
  • UpdatePlayerData is a cloud script function that I call from my iPhone simulator in Visual Studio. I'm developing a game for iOS and Android. How can I change the interval code so that it works on my environment? It's not working with "this": "TypeError: this.setInterval is not a function\n at handlers.UpdatePlayerData (BFD0A-main.js:1153:23)\n at Object.invokeFunction (Script:116:33)" – Hobbit7 Jan 04 '20 at 20:15
  • I realize now that was my error. I work with JavaScript exclusively on the client side and `window.setInterval` or `this.setInterval` only works on the client/browser side but is not required on either the browser or server side. So just remove the `this.` from calls to `setInterval` and `clearInterval`. – Booboo Jan 04 '20 at 20:26
  • Just removing the "this." doesn't work. I get an error message. I use this cloud script: https://api.playfab.com/docs/tutorials/landing-automation/using-cloud-script – Hobbit7 Jan 04 '20 at 22:54
  • I did not find anything that suggests that Cloud Script does not support setInterval. But I have three questions: (1) the call to setInterval was part of the demo to drive call to Function1; it was not integral to the `makeDebouncer` logic, which only relies on `this.setTimeout`. (2) Why did you accept my answer if you can't get this to work? (3) What is your error message? – Booboo Jan 04 '20 at 23:11
  • (1) I get an error message even if I only use function make_debouncer(fn, ms) without the call to setInterval. I have tried it with setTimeout and this.setTimeout "ReferenceError: setTimeout is not defined\n at debounce (BFD0A-main.js:1289:13)\n at debounce (BFD0A-main.js:1286:16)\n at handlers.UpdateLeaderboard (BFD0A-main.js:1170:5)\n at Object.invokeFunction (Script:116:33)" (3) "ReferenceError: setInterval is not defined\n at handlers.UpdatePlayerData (BFD0A-main.js:1154:18)\n at Object.invokeFunction (Script:116:33)" (2) I have unaccepted the answer. – Hobbit7 Jan 05 '20 at 09:11
  • The function sleep(milliseconds) in this article(Bringing Sleep to Native JavaScript) works for me if I just want to wait a few milliseconds before the next line of code is executed: https://www.sitepoint.com/delay-sleep-pause-wait/ – Hobbit7 Jan 05 '20 at 09:54
0

Lie.

Return issuccessful as true even though you did nothing.

Why wait? You updated less than four seconds ago. Waiting around for 3.9 seconds, or delaying the update for the future, is extra complexity.

The only real problem with this solution is the “final save” just before quitting the app/page. You will want a different method that does the save regardless of when the last save was.

You could pass a “noTimeout” option to this function, but then people will get into the habit of passing true.

John Burger
  • 3,662
  • 1
  • 13
  • 23