Lost in globalTIME

I have a lib I wrote call dodad.py with the lines in a class. Much left out.

def startNetwork(self):
print ‘Time packet’, packet[‘time’]
print ‘current time’, globalClock.getRealTime()
globalClock.setRealTime(packet[‘time’])
print ‘current time, after set’, globalClock.getRealTime()

This works just fine. I can tell with the print statements. BUT then I import this with second program and run the lines in the dodad.py but the second program does not show any change it the globalClock.getRealTime(). Why not? and how do I make it change is the program from the lib?

I can post all of the programs but they are a bit long.

Hope this is clear. Thanks.
Douglas

As far as I know, globalClock.setRealTime() should always have an immediate effect on the next call to globalClock.getRealTime(). If you’re not seeing a change to getRealTime(), are you sure the value you passed to setRealTime() was something different than it would have been anyway?

David

When I print from the lib I get a value of about 300.
When I print from the main, I get a value of more like 1.
The server has been running much longer and the 300 is correct. The one seems like the time the main client has been running but seems to have nothing to do with the time reset in the lib. I test it and it works in the lib.

All the files are here.
zeta-uploader.com/1677262113

Just run test_server and then test_client.

I would also be very happy for any other input about making this code better. I am a bit new to python and panda3d.
Thanks
Douglas

I don’t understand what you’re asking about, but when I run your code, I get output like this on the client:

Connected
starting polling
Time packet 86.373191
current time 0.400426
current time, after set 86.373194

Looks right to me. The client had just started (so its time was 0.40), but the server had been running for a few seconds (so its time was 86.37). After setting the time, the client is now reflecting the same time (86.37). Don’t see a problem here. Is the problem here or somewhere else?

Incidentally, I wouldn’t have solved this problem by setting the time on the client. This might muck up things like frame rate calculations, or doMethodLaters, or all sorts of things that rely on the local time computation increasing normally. (Actually, come to think of it, maybe you are getting burned by the clock regulator, which attempts to keep the clock increasing normally even when it appears to skip backwards, to compensate for certain buggy multi-core motherboards.)

A better solution would be to remember the delta between the server and the client, and add this delta to the client time whenever you want to know the server time (and vice-versa).

In fact, there is already code to handle this kind of addition in direct/src/distributed/ClockDelta.py, but it’s not terribly well-documented, and it might be easier to understand if you simply made your own implementation.

David

Yes, it works at that point. The problem point is line 31 in client_test. This gets a packet from the sever and prints the time it came in. This time is not the new time but it should be. I send a few packets over from test server to make sure.

What I am trying to do is get the system all setup to use smoothmover. I don’t understand all this very well but I thought that smoothmover needed a time that was the same on all clients and the server. I am open to knowing the best way to do it. I will look into the other stuff you said today, if I have time.

I want to make networking easy for beginners, including me. Any data on making a MMORPG (a very small one) happen would be helpful.

The SmoothMover needs to receive a timestamp that is consistent with the globalClock’s timestamp on the local machine, this is true. The best way to achieve that is to take the received timestamp from the server and then correct it by subtracting the delta.

Maintaining an explicit delta between your client’s time and the server’s time is generally better than trying to actually sync up the clocks. It gives you more power to make a distinction between things that really do need to be synced, and things that should run consistently on local time.

That said, making an MMORPG, even a very small one, is an enormous project. Even using a small part like the SmoothMover correctly will take some learning. But it might be better to get some of the other parts first: once you have avatars moving around in general, even if they are not smoothed, you will have learned enough to understand more readily how to use the SmoothMover.

David

This is not aimed at you, it is a bit of a rant, you have been really helpful but I am really sick of hearing how big a project it is. I have been working on it for 3 years plus many years before that in an idea stage. I have it working with characters moving about and all that (networked but jumpy).

Why does everyone think that it is so hard? Not only that but almost EVERY time I ask a question about a MMORPG, instead of answering the question, I get a lecture on how it can’t be done or how hard it is etc. By the next week I have usually done what I was asking about but it could have been done in a day with good help. LOL. I would be much more helpful if people just answered my or other peoples MMORPG questions. I can’t tell you how many times I have goodled my questions only to find pages of how MMORPG can’t be done by the likes of the questioner with no answer forth coming.

Yes, there is a LOT more to do but I have a whole life to do it in. I really think it is counter productive to go around telling people how hard something is. I wonder how many people gave up do to negativity? It is a lot of work but it really is not that hard so far. In is not like I am inventing nuclear fusion, there are lots of them out there already and they work. Sure it is not like writing Tetrus, that can be done in a week, but so what?

Here are a bunch of test videos from my design stages.
youtube.com/user/xomagick
I don’t have any vids from my game yet but then there is not much to see. Just a bunch of aliens running around some mountains and hills with a few trees and a spaceship.

After writing this part of my program, I realized that my networking code sucked, so I wrote what you see. It is a big step forward for me. All the communications are now simple. You just get events and send out messages. I will refine the message bit so that they can be addressed to an object (maybe), server, client, list of clients or all. Other than that and time the server/client lib code is done, I think.

The next step is to track all the clients with client records on the server and get smoothmover working. Then I will have to work with some sort of server/client avatar object code. I heard there is some in panda but have not had the chance to look at it yet.

I would still like to know why my time code does not work. Not because it must work but because I would like to understand what is going on with it. Then I will look at doing it the way you suggested.

Thanks lots and sorry about that rant.
Douglas

I took a look at ClockDelta. I can’t say I fully get it yet.
To use it I would first receive the universal time from the server and then:
godTime.resynchronize(self, localTime, networkTime, newUncertainty)

But what is the newUncertainty and how do I get it/calculate it??

other questions.
Is this clockDelta meant to be used only on the client?
Once this is set up how do I use it with smoothmover?

All questions I have now. Maybe I will get them as I make this work?

Updated code link at the bottom. I hope this makes my question clearer. I have also started using ClockDelta and it seems to work fine and is easy. With about 5 more sentances this could be fully documented. I still need to have some info on its finer points. I read the source but that did not help much with how you use it.

How often do you resync?

NewDelta? What is this for?

what is peerToPeerResync and why and when do you use it?

What is Uncertainty and trust?

The time went negative at the ending of my testing. Why was that? Seems to be related to my setting global time. I still want to understand why that does not work. Without the global time change on the client the deltaClock seems to work fine.

zeta-uploader.com/769508701

Thanks for your help. I guess you are sleeping now and will wake to a full box of my questions. :slight_smile:
Douglas

Well, I do apologize for the negativity regarding MMORPG development. There’s nothing wrong with walking down the long road that is developing an MMO, of course; and you have all of my encouragement. I understand how discouraging it can be that the internet appears to be filled with people trying to tell you how hard it is. On the flip side, the internet also appears to be filled with people who have absolutely no idea how hard it is; and no doubt the negativity is a reaction to this naive enthusiasm.

Now, to your specific questions. I’ll answer the last one first, since this is useful to understand the rest:

What is Uncertainty and trust?

Uncertainty is the number that indicates how precise your measurement of server time is. You should understand that no two clocks are ever precisely in sync, and you also cannot know precisely how far out of sync you are. If the server sends a message to the client saying it is precisely 12:00, by the time the client receives the message, it is already out of date. Since you don’t know precisely how long it took for the message to get from the client to the server, you don’t know precisely how far out of date it is, and therefore the client doesn’t know precisely what time it is on the server right now.

But you can put an upper bounds on time it took to receive the message. For instance, suppose the client starts a timer, then sends a message to the server asking what time it is. When the server receives this message, it immediately sends a response to the client indicating the server’s current timestamp. When the cilent receives this response, it stops its timer and looks at how long the round-trip message took.

That round-trip message time is the uncertainty. The server responded sometime after the first message was sent from the client, so you know that the message can’t be more than n seconds old. (You could guess that both messages took the same amount of time to send, and assume that the message was exactly n/2 seconds old, but this wouldn’t be realistic. In fact, some messages take a lot longer to transmit than others.)

Once you have a measure of the uncertainty, you can say that your client’s clock is in sync with the server, +/- n seconds.

The trustNew parameter to resynchronize() just describes what to do if the new time measurement is completely different from the previous one. If trustNew is True, then the previous time measurement is discarded, and the new one is accepted. If trustNew is False, the previous measurement is kept, and the new one is discarded. See peerToPeerResync, below.

I don’t know. How much clock drift can your application tolerate? Any two clocks, once synced, will gradually drift apart from each other. That is to say, over time, the uncertainty will gradually increase. Thus, you have to sync occasionally to keep them from drifting too far apart. The frequency of resync depends on two points: (1) the rate at which your two clocks are drifting apart, and (2) the tolerance your application has for clock drift.

In practice, probably a resync every 30 minutes or so will be more than sufficient.

This method is used internally by resynchronize(). You normally wouldn’t call it directly.

If your application involves any direct communication between peers (two or more clients, as opposed to only client-server communication), then you can use this method to pass a sync from one client to another. This is a little less good than receiving a sync directly from the server, because you inherit the uncertainty from the other client, plus the uncertainty of the network communications. But it’s conceivable that client A can’t get a good reliable connection to the server, but he can get a reliable connection to client B, and client B can get a reliable connection to the server.

Of course, since the other clients might be lying (in any MMO, you can’t trust any of the clients, since they might be corrupted by hackers), peerToPeerResync automatically passes trustNew = False.

On the whole, though, peerToPeerResync is a little esoteric.

David

Well, I can’t speculate what went wrong to make the time go negative, but as to the global time, it’s simply that the global time clock is used by a lot of different systems within Panda. If you start monkeying with that, you can confuse a lot of different things. If you want, though, you can create your own ClockObject instance (separate from the globalClock object), and call setRealTime() on that. Then you can use this object instead of the ClockDelta class to keep track of your server time.

David

OK, I added the smoothMover but nothing is smooth. What did I miss? I know that the godTime is correct with the server. I think I must not be getting how to use SmoothMover.
Thanks again. Seems like you should just write the manual and then not have to answer these types of questions over again but anyway, thank you!
Douglas

    def Move(self, task):
        self.playerMoved = True # They changed possition so it is time to transmit data about the player

        # SMOOTH STUFF TEST
        #self.face.showBounds()
        self.smoothMover.computeSmoothPosition()
        self.face.setHpr(self.smoothMover.getSmoothHpr())
        self.face.setPos(self.smoothMover.getSmoothPos())

        elapsed = task.time - self.prevtime
        self.avatar.z = self.terrain.getElevation(base.camera.getX(), base.camera.getY()) * self.ground.getSz() + .5
        base.camera.setZ(self.terrain.getElevation(base.camera.getX(), base.camera.getY()) * self.ground.getSz() + .5)

    def networkDataIn(self, dataIn):
        self.count += 1
        # Print results

        # print dataIn, 'current real time', globalClock.getRealTime(), 'god time', self.client.godClock.getRealNetworkTime()
        print "client in: ", dataIn, self.client.ID
        if dataIn["PacketType"] == "PlayerData" and dataIn['playerID'] != self.client.ID :

            h,p,r = dataIn["hpr"]
            x,y,z = dataIn['xyz']

            self.smoothMover.setPos(x, y, z)
            self.smoothMover.setHpr(h, p, r)
            self.smoothMover.setTimestamp(dataIn['godTime'])
            self.smoothMover.markPosition()

    # Data Out
    def sendDataTask(self, task):
        if self.playerMoved == True :
            self.playerMoved = False
            dataOut = {}
            dataOut["PacketType"] = "PlayerData"
            dataOut["playerID"] =  self.client.ID
            dataOut["xyz"] = tuple(base.camera.getPos())
            dataOut["hpr"] = tuple(base.camera.getHpr())
            dataOut['godTime'] = self.client.godClock.getRealNetworkTime()

            # Send data to the server
            self.client.sendData(dataOut)
            #print "client out: ", dataOut
        return Task.again

Hmm, well, the SmoothMover is a complicated beast, and there might be lots of different things that can make it fail.

The first thing I see with a cursory look is that you are feeding dataIn[‘godTime’] directly as the timestamp value. However, this is the time value encoded in “network time”, which is a special representation designed for efficient transmitting of time values on the network, but which is not in the same scale as your globalClock. You must convert this network timestamp to a local timestamp by using ClockDelta.networkToLocalTime(). You can also print this value after you have converted it back, to confirm that it is reasonably consistent with the time reported by globalClock.getRealTime().

Otherwise, the code looks reasonably correct at a glance. If changing the above does not solve the problem, then there is probably something more subtle going on, and it will take some investigation to discover it. Note that you can print the smoothMover to display the list of timestamp values it thinks it is smoothing between; this may aid debugging. You might also try setting:

notify-level-deadrec debug

or even:

notify-level-deadrec spam

in your Config.prc file, to enable verbose debugging information in this module.

David

I am getting a bit confused.

#There is Server time here being sent to the client at start up.
self.sendData({"time":globalClock.getRealTime(), 'ID':self.connectionCount}, newConnection)

#there is Server time that is sent to the client and called 'god time'.
self.godClock.resynchronize(globalClock.getRealTime(), packet['time'], 0.2)
                    #globalClock.setRealTime(packet['time'])
print 'god time', self.godClock.getRealNetworkTime()

#there is client time for each client.
globalClock.getRealTime()

So what does SmoothMover take and where do I put the :

ClockDelta.networkToLocalTime()

Also I guess I should go an research more what Frame time is. Is that relevant here?
Thanks, Douglas

OK, so, a bit of context about the different kinds of time.

You have basically three types of time here: (a) server time, (b) client time, © network time. The server time is globalClock.getRealTime() on the server; the client time is globalClock.getRealTime() on the client (or getFrameTime(), see below). These two clocks are independent and have different values, of course, but they are both floating-point numbers that track time in seconds. Then you have network time, which is neither of these, and isn’t even a time in seconds. It’s just an encoding of either (a) or (b) into some context-neutral representation. It happens to be an integer value, but if you looked at the value it wouldn’t mean anything to you. And that doesn’t matter, because you never need to look at it.

The idea is that you use network time whenever you need to transmit a time value from client to server and vice-versa. Network time is simply a clock-neutral representation, so anyone can convert from their own clock into network time, and anyone can convert from network time back into their own clock.

So, for example, the client never thinks about server time. All of the client’s operations are performed in client time. When the client sends a timestamp to the server, he converts it first to network time, and sends the network time. When the client receives a timestamp from the server, he receives the network time value, and then converts it to client time.

A similar operation happens on the server.

Anytime you put any timestamp on the wire, you first convert it to network time, and send the network time value instead. Anytime you receive a timestamp from the wire, you receive the network time value, and then convert it to local time before you use it. This extends to the initial sync operation as well; even then, you’re only dealing with network time. There’s never any need to send a getRealTime() or getFrameTime() value over the network.

As to the real time vs. frame time: yes, you should probably be using frame time instead of real time, in most cases. The difference is subtle, though, and you can get away with real time if you don’t yet understand (or want to think about) the difference. I’ll try to explain it briefly.

Real time is continuously updated. Frame time updates discretely, in fact, it is updated only at the beginning of the frame. This means that frame time is a frozen image of the clock as it was at the beginning of the current frame.

For virtually all animations, you really want to use frame time, because the frame you will eventually render is supposed to represent a single moment in time, just as if it had been captured by a movie camera. Even though it might take you 0.2 seconds to compute the frame, you don’t want the resulting frame to look like that. Imagine you are rendering a horse race. You have a line of horses on a racetrack, who all started at the same moment and all run at the same speed. You still want them all to be drawn neck-and-neck in each frame, even though it might take you a bit of time to calculate the position of each one. If you used real time to compute each horse’s position, then the last horse you computed would appear to be a little in front of all of the rest of them. If you used frame time, though, they would all be drawn exactly where they were at the beginning of the frame, which is all neck-and-neck.

So, any time computation that directly affects the rendered appearance should use frame time. This includes character animation, interval lerping, physics, movement in general. Including the SmoothMover, of course. In fact, in virtually every case when you query the time, you really want the frame time. The only exceptions are when you are actually timing something that happens inside of a frame, which is pretty rare. PStats uses real time, for instance. You will use real time for your initial clock sync. But virtually everywhere else, you should get used to the idea of using frame time.

David

I still can’t get the programs to sync up. This is how I am setting resync in the client’s program. I think it is correct. It seems when I run 3 instances of my client that they each have different Network times. I think they should all have the same network time. I am about ready to just send you all my code and beg for help.

# has local real Time of when the message was sent
sent = dataIn['sendTime']

# has local real Time of the server when the server reflected it back
# maybe it should be something else that global real time???

serverTime = dataIn['serverTime']

# has loacl real time that the message was recieved.
now = globalClock.getRealTime() 

# amount of time trip took in total
tripTime = now-sent 

print 'sent, server, now, Trip Time', sent, serverTime, now, tripTime

# we need to bounce a packet off the server to see
# how long the transit time is to set the uncentaty setting.
# This must be redone about every 30 minutes.

#These 2 line is not really in my program
serverTime=self.client.godClock.localToNetworkTime(serverTime)
self.client.godClock.resynchronize(now, serverTime, tripTime)

# instead I am using 
self.client.godClock.newDelta(now, serverTime-now, tripTime)
#But it should be the same thing. 

This is my code to send out the player’s position, send from the player’s client.

dataOut['xyz'] = tuple(base.camera.getPos())
dataOut['hpr'] = tuple(base.camera.getHpr())
dataOut['networkTime'] = self.client.godClock.getFrameNetworkTime()

And, this is my code for taking in the other players positions into smoothMover, on the same player’s client. All this code is in the same client program.

self.avatar[player].smoothMover.setPosHpr(dataIn['xyz'], dataIn["hpr"])
self.avatar[player].smoothMover.setTimestamp(self.client.godClock.networkToLocalTime(dataIn['networkTime'])) # This should be network time converted to local time.
self.avatar[player].smoothMover.markPosition()

To add some detail what is happening is that when I start 2 clients, and move in the first one, the movement appears in the second one a few seconds later but when I move in the second one, then the movement happens almost at once in the first program. I don’t understand why one has this big delay and the other no delay.

Oops, sorry, I forgot to respond to this. Give me a little time to answer it carefully.

David

No problem, I am working on learning and getting the state machines working. I have a bad looking but working smooth system now.