How can we view a multiplayer game replay in reverse? - networking

We are building a multiplayer game that supports replays. The replays work like this:
All state updates from the server get saved into a file. When we are replaying the file we just take the initial state recieved from the server and apply the state updates. This works fine. We can play, fast-forward, pause and go ba... oh wait.
What would be the best way to actually allow players to view the replay backwards?
Some implementation details, we use a huge state struct (well, not really, but you could see it as such). This struct gets updated over the network (UDP) by calculating differences between the last sent struct and the current one. The client interpolates between the last two recieved structs and extrapolates when needed (when ping > throttle of updates). In addition we use an acked event system for stuff that hasn't to be drawn smoothly on the screen but needs to be, well, acked.

A couple of suggestions (assuming you can make a snapshot at any point, and you're storing forward deltas).
1.. If the replay is short and the state isn't too big, actually generate snapshots for each frame in the sequence and "render" them in reverse.
2.. If the whole state is very big, then only generate a snapshot for the last frame of the reversed replay. Then apply X forward deltas to the snap shot to generate the first frame of the reversed replay. Render that. Then apply X-1 deltas, and render that, repeat until X = 0, when you're at the end of replay (just the first snap shot).
// pseudo code for reverse play
first, last = GetReplayRange();
if (playReversed) {
GameState snapShot = GenerateSnapshot(first);
GameState currentState;
currentFrame = last;
while (currentFrame != first) {
// loop from snapshot up to current frame
for (i = first; i < currentFrame; i++) {
currentState = snapShot.AddDetlasTo(currentFrame);
}
Render(currentState);
currentFrame--;
}
}
3.. If its too slow to apply X updates to a snapshots to "rewind" then make a series of snap shots N frames apart, and do the same reverse trick in #2, over a narrower range.
Remember that even if you have a reversed game state ready to render - you'll still have to render all your effects in reverse, which most effect systems probably aren't geared for.

Related

How to gracefully shut down reactive kafka-consumer and commit last processed record?

My painful hunt for this feature is fully described in disgustingly log question: Several last offsets aren't getting commited with reactive kafka and it shows my multiple attemps with different failures.
How would one subscribe to ReactiveKafkaConsumerTemplate<String, String>, which will process the records in synchronous way (for simplicity), and will ack/commit every 2s AND upon manual cancellation of stream? Ie. it works, ack/commits every 2s. Then via rest/jmx/whatever comes signal, the stream terminates and ack/commits the last processed kafka record.
After a lot of attempts I was able to come up with following solution. It seems to work, but it's kinda ugly, because it's very "white-box" where outer flow highly depends on stuff happening inside of other methods. Please criticise and suggest improvements. Thanks.
kafkaReceiver.receive()
.flatMapSequential(receivedKafkaRecord -> processKafkaRecord(receivedKafkaRecord), 16)
.takeWhile(e-> !stopped)
.sample(configuration.getKafkaConfiguration().getCommitInterval())
.concatMap(offset -> {
log.debug("ack/commit offset {}", offset.offset());
offset.acknowledge();
return offset.commit();
})
.doOnTerminate(()-> log.info("stopped."));
What didn't work:
A) you cannot use Disposable.dispose, since that would break the stream and your latest processed record won't be committed.
B) you cannot put take on top of stream, as that would cancel the stream and you won't be able to commit either.
C) not sure how I'd be able to intercorporate usage of errors here.
Because of what didn't work stream termination is triggered by boolean field named stopped, which can be set anyhow.
Flow explained:
flatMapSequential — because of inner parallelism and necessity to commit N only if all N-1 was processed.
processKafkaRecord returns Mono<ReceiverOffset>, ie. the offset of processed record to have something to ack/commit. When stopped the method will skip processing and return Mono.empty
take will stop stream if stopped, this has to be put here becaue of possibility of whole sample interval consisting only from "empties"
rest is simple: sample by given interval, commit in order. If sample does return empty record, commit is skipped. Finally we log that stream is cancelled.
If anyone know how to improve, please criticise.

FRP vs. State Machine w/ Lenses for Game Loop

I'm trying to understand the practical difference between a FRP graph and a State Machine with lenses- specifically for something like a game loop where the entire state is re-drawn every tick.
Using javascript syntax, the following implementations would both essentially work:
Option 1: State Machine w/ Lenses
//Using Sanctuary and partial.lenses (or Ramda) primitives
//Each update takes the state, modifies it with a lens, and returns it
let state = initialValues;
eventSource.addEventListener(update, () => {
state = S.pipe([
updateCharacter,
updateBackground,
])
(state) //the first call has the initial settings
render(state);
});
Option 2: FRP
//Using Sodium primitives
//It's possible this isn't the best way to structure it, feel free to advise
cCharacter = sUpdate.accum(initialCharacter, updateCharacter)
cBackground = sUpdate.accum(initialBackground, updateBackground)
cState = cCharacter.lift(cBackground, mergeGameObjects)
cState.listen(render)
I see that Option 1 allows any update to get or set data anywhere in the game state, however all the cells/behaviors in Option 2 could be adjusted to be of type GameState and then the same thing applies. If this were the case, then I'm really confused about the difference since that would then just boil down to:
cGameState = sUpdate
.accum(initialGameState, S.pipe(...updates))
.listen(render)
And then they're really very equivalent...
Another way to achieve that goal would be to store all the Cells in some global reference, and then any other cell could sample them for reading. New updates could be propagated for communicating. That solution also feels quite similar to Option 1 at the end of the day.
Is there a way to structure the FRP graph in such a way that it offers clear advantages over the event-driven state machine, in this scenario?
I'm not quite sure what your question is, also because you keep changing the second example in your explanatory text.
In any case, the key benefit of the FRP approach — as I see it — is the following: The game state depends on many things, but they are all listed explicitly on the right-hand side of the definition of cGameState.
In contrast, in the imperative style, you have a global variable state which may or may not be changed by code that is not shown in the snippet you just presented. For all I know, the next line could be
eventSource2.addEventListener(update, () => { state = state + 1; })
and the game state suddenly depends on a second event source, a fact that is not apparent from the snippet you showed. This cannot happen in the FRP example: All dependencies of cGameState are explicit on the right-hand side. (They may be very complicated, sure, but at least they are explicit.)

Programming: Detect the direction and stop of change of a number

Any language, just pseudocode.
I'm searching around for an algorithm that detects direction and the stop of changes to a number. E.g.:
function detectChange(int number) {
if number is rising return "rising"
if number is dropping return "dropping"
if number is unchanged return "unchanged"
}
main() {
int number
while(true) {
//The read doesn't always happen
if readObscure.readoccured() {
//read the number from an obscure source
number = readObscure()
print(detectChange(number))
}
}
}
I've been working on an approach with a time delta but with little success. One problem is e.g. that with the timing approach I always miss the last change. Maybe I could solve that too but it's already pretty hacky.
So I'd be glad about a clean "textbook" solution, preferably without using time but just logic. If there's none without time, but still a clean solution, I'd appreciate that too.
Solution can be written in any "human readable" language (no haskell please) or pseudocode, I don't care.
I should have mentioned that the readObscure() function may also return the same number over and over again, or won't return a number at all, in which case I want to assume that the number is "unchanged".
Let's also update this with some examples:
readObscure() returns the numbers 1,2,14,15,8,17,20
This should be "rising"
readObscure() returns the numbers 1,2,14,15,17,20,20,20
This should be "rising" and then "unchanged"
So the question also is, how to define rising, unchanged, dropping. I'd like someone who maybe worked on those problems before to define it. The result should equal a "human sorting", so I look at the numbers and can immediatly tell, they are not rising, or they are rising.
I've been made aware of Rx (Reactive Extensions)
But for my personal case this is using a sledge-hammer to crack a nut.
Just make it so that whenever you add a value:
Take the value of the current and the last value, compute its delta.
Then, add it to wherever you're holding the deltas.
If you want something to "fire" everytime you "add a value," it's probably best to bind it to the container or some sort of callback/event-based mechanism/structure to ensure this. Boost.Signals2 (C++) is supposed to be a good way to handle this, but something as simple as creating an asynchronous thread of execution to compute and then push your value to the back of the storage vector would be good enough.

RPC synchronization alternative MMORPG, Photon Unity3D

I'm using Photon Unity Networking and I am working on a little game of mine.
I got to a point where I have a room with players and mobs.
When a new player appears I use RPC call to update information about this player to all other connected users to get synchronized.
The problem is.. that this new player does not have any information about the rest of the room (his info is not up to date).
I mean for instance current health of other players, or current health of mobs, etc.
The only only solution I came up with is to send an RPC to a master client, pass through all volatile objects around and send several RPC calls back to the new player with this update.
What I am asking is... do I really have to it like this? Or is there any other way, any better or simpler way?
Okay so the phonton networking works via photon network view - and its observed components, means scripts
in this observed script you have to pass (if its your character and you are controlling it)
m_PhotonView = GetComponent<PhotonView>(); //Variable
if( m_PhotonView.isMine == true ) //in Void Update()
all variables you need, position, rotation, name, health, relevant data for animations and so on by using SetSynchronizedValues()
Variable = GetComponent<PhotonTransformView>();
Variable .SetSynchronizedValues( Position, Health , Name);
and it will synchronise the Variables, then you have to use them (display the name, set the object to the correct position , show a health bar and resize it) if it's an non controlled character only
if( m_PhotonView.isMine == false)
Hope I could help you

Purely functional feedback suppression?

I have a problem that I can solve reasonably easy with classic imperative programming using state: I'm writing a co-browsing app that shares URL's between several nodes. The program has a module for communication that I call link and for browser handling that I call browser. Now when a URL arrives in link i use the browser module to tell the
actual web browser to start loading the URL.
The actual browser will trigger the navigation detection that the incoming URL has started to load, and hence will immediately be presented as a candidate for sending to the other side. That must be avoided, since it would create an infinite loop of link-following to the same URL, along the line of the following (very conceptualized) pseudo-code (it's Javascript, but please consider that a somewhat irrelevant implementation detail):
actualWebBrowser.urlListen.gotURL(function(url) {
// Browser delivered an URL
browser.process(url);
});
link.receivedAnURL(function(url) {
actualWebBrowser.loadURL(url); // will eventually trigger above listener
});
What I did first wast to store every incoming URL in browser and simply eat the URL immediately when it arrives, then remove it from a 'received' list in browser, along the lines of this:
browser.recents = {} // <--- mutable state
browser.recentsExpiry = 40000;
browser.doSend = function(url) {
now = (new Date).getTime();
link.sendURL(url); // <-- URL goes out on the network
// Side-effect, mutating module state, clumsy clean up mechanism :(
browser.recents[url] = now;
setTimeout(function() { delete browser.recents[url] }, browser.recentsExpiry);
return true;
}
browser.process = function(url) {
if(/* sanity checks on `url`*/) {
now = (new Date).getTime();
var duplicate = browser.recents[url];
if(! duplicate) return browser.doSend(url);
if((now - duplicate_t) > browser.recentsExpiry) {
return browser.doSend(url);
}
return false;
}
}
It works but I'm a bit disappointed by my solution because of my habitual use of mutable state in browser. Is there a "Better Way (tm)" using immutable data structures/functional programming or the like for a situation like this?
A more functional approach to handling long-lived state is to use it as a parameter to a recursive function, and have one execution of the function responsible for handling a single "action" of some kind, then calling itself again with the new state.
F#'s MailboxProcessor is one example of this kind of approach. However it does depend on having the processing happen on an independent thread which isn't the same as the event-driven style of your code.
As you identify, the setTimeout in your code complicates the state management. One way you could simplify this out is to instead have browser.process filter out any timed-out URLs before it does anything else. That would also eliminate the need for the extra timeout check on the specific URL it is processing.
Even if you can't eliminate mutable state from your code entirely, you should think carefully about the scope and lifetime of that state.
For example might you want multiple independent browsers? If so you should think about how the recents set can be encapsulated to just belong to a single browser, so that you don't get collisions. Even if you don't need multiple ones for your actual application, this might help testability.
There are various ways you might keep the state private to a specific browser, depending in part on what features the language has available. For example in a language with objects a natural way would be to make it a private member of a browser object.

Resources