From: Erich C. <3l...@sp...> - 2004-06-21 20:23:01
|
One issue that's going to be coming right up is the details of game state communication. Should remorse be hardwired for 'actors' that have position, movement state, etc. Or should remorse have a more flexible approach like a binary xml that allows arbitrary payload? Maybe both eventually. Artillery and combat will use pretty similar state-updates, I would think. But if someone does a trivia game, that'd be rather different. My suspicion is that it will be well worth our while to be very efficient here. If I move my tank, I shouldn't have to explicitly communicate that I'm not moving. So I'm sort of thinking that we need to have operations of the form 'actor, aspect, change' eg 'tank1, facing, 397' (we're using gradians right?) and part of the modules job is to convert between method calls and transmission modes. This leads right into 'where exactly is the world state' type questions. I'm still thinking in terms of: 1. I press spacebar 2. Local input module translate this into 'fire' based on my config 3. Local game module converts to request 'elc's tank, fire cannon, true' 4. Local engine sends request to server 5. Server dumps request into server's game module 6. Server's game module confirms that I'm ready to fire, still alive, and so forth, changes my state to firing, adds world state update to some kind of broadcast queue. (also, adds a notice about the existence and position of a tac-nuke shell, but that's another story) 7. Server engine sends elc firing update to all players 8. Local engine recieves 'elc's tank, firing cannon, true' message and delivers it to local game 9. Local game exhibits appropriate beeps and flashes. That's probably maximal server involvement, but otherwise we enter the realm of jumping backward in time when my code indicated I fired but your code indicated your satellite EMP'd me first. Also, I take it we prefer a mailing list to using the dev forum? ec |
From: josh g. <jo...@gm...> - 2004-06-23 08:36:53
|
Be warned, another long one ahead=E2=80=A6 > Should remorse be hardwired for 'actors' that have position, movement sta= te, > etc. Or should remorse have a more flexible approach like a binary xml= =E2=80=A6 I'll suggest this article again, since it addresses this topic specifically= : http://www.gamasutra.com/resource_guide/20020916/lambright_01.htm It's weird, I was surfing the gamasutra site the other day and wasn't asked to log in=E2=80=A6 Now it's asking me for a password. I went ahead = and signed up, since the site has a lot of good stuff on it. I recommend it. Anyway, this article describes a model for managing distributed state that abstracts the nature of the objects being distributed from the distribution agent. It seems like a simple, generic, and efficient solution that lends itself nicely to a general purpose gaming framework. I'll describe it here as briefly as I can, but I recommend you read the article for yourself=E2=80=A6 For every type of game state that must be synched between client and server, the developer must also provide a "Transport" class to communicate state changes. (The author calls them "Object Views," but I'm not crazy about that name because "view" also means something different to me=E2=80=A6 Got a better name?) The scope of a Transport cla= ss should correspond to a set of state variables that are related; most common will be a single Transport class for each game object class (eg, Tank <--> TankTransport), but nothing prevents the developer from splitting or combining the Transports arbitrarily. For example, a Tank object's state might be distributed by a TankTurretTransport and a TankPositionTransport. For simplicity in this discussion, I'll assume that each Transport corresponds to a specific game object. These Transports always exist in remote pairs: every instance on the server always corresponds to exactly one remote instance on exactly one client. Thus, multiple Transport instances may exist on the server for a single game object, one instance for each client with a copy of that game object. (For simplistic games like the ones we're currently building, we can assume that EVERY server-side game object will have Transport instances for every client in the game. This is because each client will always maintain a complete world-model. Note that this wouldn't be the case for larger MMOGs, where each client might only know about a small fraction of the objects in the world.) The role of the Transport is this: it keeps track of the state that was last synched with its remote counterpart, and it handles the packing and unpacking of state-change data for wire transmission. When the server's distribution agent is ready to update a particular client, it first identifies all of the local Transports that are assigned to that client. It then asks each of those Transports for a list of changes to be delivered to the client. It does this by passing a buffer to each Transport, into which the Transport must write its change-list as concisely as possible. Thus, each Transport instance must be able to inspect its game object and determine what aspects of its state have changed since it last sent an update. The way that it accomplishes this is left to the specific Transport class, but a few obvious techniques are readily apparent: state-caching, and change-counting. I won't discuss these in detail=E2=80=A6 Anyway, the contents of the change-list buffer are opaque to the distribution agent; it just transmits the data to the corresponding Transport object on the other side. The receiving Transport is responsible for interpreting the information and applying the correct changes to its local game object. Thus, the Transport interface consists of two methods: unpackFrom(Buffer) and packTo(Buffer). In unpackFrom, the Transport must read in a list of changes and apply them to the local game object. In packTo, the Transport must detect and write out all local changes that have occurred on the game object since the last time packTo (or unpackFrom) was called. The system for communicating client-side user input to the server is identical: when a client's copy of a game object is changed because of user input, the associated Transport will be ready to transmit a state update to its server-side counterpart. A client-side distribution agent will call its packTo method to fill a buffer with the changes, then send the buffer to the server's Transport for the object. The server's Transport will update the server object, which will trigger the Transports for all of the other clients to become ready to distribute the update=E2=80=A6 So, to mimic Erich's tac-nuke example, the steps involved might look something like this: (1) I press mouse1 (2) Local input module translates this to 'fire' based on my config, and (indirectly) executes the "fire()" method on my currently active Tank object (player[1].tank[3]). (3) tank[3] changes its state to "firing," and perhaps sends a StateChangeEvent to all listeners (which might include the local distribution agent or the TankTransport object assigned to this tank... I'm not sure). (4) The distribution agent somehow recognizes that it needs to update the server, and that tankTransport[3] has an update to send, so it passes a ByteBuffer to the tankTransport[3].packTo() method. (5) TankTransport.packTo() determines that its game object, tank[3], attempted to fire a shot at time 8573, angle 0.796, shotpower 88, shell-mass 1.8. It packs this information into the buffer provided (or whatever information is interesting for the server). (6) The distribution agent encapsulates the data in a message, addresses the message to shellshock[1].player[1].tankTransport[3] (or something), and sends it off to the server. (7) The server locates the Transport called shellshock[1].player[1].tankTransport[3], and invokes its unpackFrom() method with the ByteBuffer extracted from the message. (8) player[1].tankTransport[3].unpackFrom() decodes the buffer and changes its local copy of tank3 to reflect the firing state. (9) tank3 on the server sends out a StateChangeEvent which somehow flags player[2].tankTransport[3] (the proxy for player 2's copy of tank[3]). (10) The server's distribution agent calls player[2].tankTransport[3].packTo(), and delivers the state change to player 2=E2=80=A6 Advantages of this design: Abstracts the distribution agent from the game specifics Potentially allows multiple games to be served by the same distribution agent (may not really be a good thing=E2=80=A6) If we get advanced, allows state updates to be prioritized, so that more important changes will be delivered first, and less important changes may be coalesced or dropped under load=E2=80=A6 (See the article f= or details) Disadvantages: Requires a lot of wire-protocol work on the part of the Transport developer (although the framework could provide utilities for this) Seems to me, user-induced state changes must be applied directly to client-side objects (see steps 2 and 3 above). To avoid inconsistency, this means that the client-side objects must have states that indicate the *intention* of doing something that may or may not take place. For example, my Tank must enter a "Firing" state that may, or may not, result in a shot being fired on the server. =20 A lot of extra objects floating around! Whew! =20 Do you guys have any feedback? I'm not attached to this idea, but the author of the article seems to know what he's talking about=E2=80=A6 -j On Mon, 21 Jun 2004 16:22:43 -0400, Erich Cranor <3l...@sp...> wrote: >=20 > One issue that's going to be coming right up is the details of game state > communication. >=20 > Should remorse be hardwired for 'actors' that have position, movement sta= te, > etc. Or should remorse have a more flexible approach like a binary xml t= hat > allows arbitrary payload? Maybe both eventually. Artillery and combat w= ill > use pretty similar state-updates, I would think. But if someone does a t= rivia > game, that'd be rather different. >=20 > My suspicion is that it will be well worth our while to be very efficient= here. > If I move my tank, I shouldn't have to explicitly communicate that I'm no= t > moving. So I'm sort of thinking that we need to have operations of the = form > 'actor, aspect, change' eg 'tank1, facing, 397' (we're using gradians rig= ht?) > and part of the modules job is to convert between method calls and transm= ission > modes. >=20 > This leads right into 'where exactly is the world state' type questions. = I'm > still thinking in terms of: >=20 > 1. I press spacebar > 2. Local input module translate this into 'fire' based on my config > 3. Local game module converts to request 'elc's tank, fire cannon, true' > 4. Local engine sends request to server > 5. Server dumps request into server's game module > 6. Server's game module confirms that I'm ready to fire, still alive, and= so > forth, changes my state to firing, adds world state update to some kind o= f > broadcast queue. (also, adds a notice about the existence and position of= a > tac-nuke shell, but that's another story) > 7. Server engine sends elc firing update to all players > 8. Local engine recieves 'elc's tank, firing cannon, true' message and de= livers > it to local game > 9. Local game exhibits appropriate beeps and flashes. >=20 > That's probably maximal server involvement, but otherwise we enter the re= alm of > jumping backward in time when my code indicated I fired but your code ind= icated > your satellite EMP'd me first. >=20 > Also, I take it we prefer a mailing list to using the dev forum? >=20 > ec >=20 > ------------------------------------------------------- > This SF.Net email sponsored by Black Hat Briefings & Training. > Attend Black Hat Briefings & Training, Las Vegas July 24-29 - > digital self defense, top technical experts, no vendor pitches, > unmatched networking opportunities. Visit www.blackhat.com > _______________________________________________ > Remorse-development mailing list > Rem...@li... > https://lists.sourceforge.net/lists/listinfo/remorse-development > |
From: josh g. <jo...@gm...> - 2004-06-23 08:43:20
|
Oh boy, I forgot to mention that the server- and client-side game object classes need not be identical. In fact, I think they will often be different, because the client-side versions can't actually impose major changes on the world; only the server-side versions can do that, to avoid inconsistency. -j On Wed, 23 Jun 2004 01:36:51 -0700, josh gruenberg <jo...@gm...> wrote= : >=20 > Be warned, another long one ahead=E2=80=A6 >=20 > > Should remorse be hardwired for 'actors' that have position, movement s= tate, > > etc. Or should remorse have a more flexible approach like a binary xml= =E2=80=A6 >=20 > I'll suggest this article again, since it addresses this topic specifical= ly: >=20 > http://www.gamasutra.com/resource_guide/20020916/lambright_01.htm >=20 > It's weird, I was surfing the gamasutra site the other day and wasn't > asked to log in=E2=80=A6 Now it's asking me for a password. I went ahea= d and > signed up, since the site has a lot of good stuff on it. I recommend > it. >=20 > Anyway, this article describes a model for managing distributed state > that abstracts the nature of the objects being distributed from the > distribution agent. It seems like a simple, generic, and efficient > solution that lends itself nicely to a general purpose gaming > framework. I'll describe it here as briefly as I can, but I recommend > you read the article for yourself=E2=80=A6 >=20 > For every type of game state that must be synched between client and > server, the developer must also provide a "Transport" class to > communicate state changes. (The author calls them "Object Views," but > I'm not crazy about that name because "view" also means something > different to me=E2=80=A6 Got a better name?) The scope of a Transport c= lass > should correspond to a set of state variables that are related; most > common will be a single Transport class for each game object class > (eg, Tank <--> TankTransport), but nothing prevents the developer from > splitting or combining the Transports arbitrarily. For example, a > Tank object's state might be distributed by a TankTurretTransport and > a TankPositionTransport. For simplicity in this discussion, I'll > assume that each Transport corresponds to a specific game object. >=20 > These Transports always exist in remote pairs: every instance on the > server always corresponds to exactly one remote instance on exactly > one client. Thus, multiple Transport instances may exist on the > server for a single game object, one instance for each client with a > copy of that game object. >=20 > (For simplistic games like the ones we're currently building, we can > assume that EVERY server-side game object will have Transport > instances for every client in the game. This is because each client > will always maintain a complete world-model. Note that this wouldn't > be the case for larger MMOGs, where each client might only know about > a small fraction of the objects in the world.) >=20 > The role of the Transport is this: it keeps track of the state that > was last synched with its remote counterpart, and it handles the > packing and unpacking of state-change data for wire transmission. >=20 > When the server's distribution agent is ready to update a particular > client, it first identifies all of the local Transports that are > assigned to that client. It then asks each of those Transports for a > list of changes to be delivered to the client. It does this by > passing a buffer to each Transport, into which the Transport must > write its change-list as concisely as possible. >=20 > Thus, each Transport instance must be able to inspect its game object > and determine what aspects of its state have changed since it last > sent an update. The way that it accomplishes this is left to the > specific Transport class, but a few obvious techniques are readily > apparent: state-caching, and change-counting. I won't discuss these > in detail=E2=80=A6 >=20 > Anyway, the contents of the change-list buffer are opaque to the > distribution agent; it just transmits the data to the corresponding > Transport object on the other side. The receiving Transport is > responsible for interpreting the information and applying the correct > changes to its local game object. >=20 > Thus, the Transport interface consists of two methods: > unpackFrom(Buffer) and packTo(Buffer). In unpackFrom, the Transport > must read in a list of changes and apply them to the local game > object. In packTo, the Transport must detect and write out all local > changes that have occurred on the game object since the last time > packTo (or unpackFrom) was called. >=20 > The system for communicating client-side user input to the server is > identical: when a client's copy of a game object is changed because of > user input, the associated Transport will be ready to transmit a state > update to its server-side counterpart. A client-side distribution > agent will call its packTo method to fill a buffer with the changes, > then send the buffer to the server's Transport for the object. The > server's Transport will update the server object, which will trigger > the Transports for all of the other clients to become ready to > distribute the update=E2=80=A6 >=20 > So, to mimic Erich's tac-nuke example, the steps involved might look > something like this: >=20 > (1) I press mouse1 > (2) Local input module translates this to 'fire' based on my config, > and (indirectly) executes the "fire()" method on my currently active > Tank object (player[1].tank[3]). > (3) tank[3] changes its state to "firing," and perhaps sends a > StateChangeEvent to all listeners (which might include the local > distribution agent or the TankTransport object assigned to this > tank... I'm not sure). > (4) The distribution agent somehow recognizes that it needs to update > the server, and that tankTransport[3] has an update to send, so it > passes a ByteBuffer to the tankTransport[3].packTo() method. > (5) TankTransport.packTo() determines that its game object, tank[3], > attempted to fire a shot at time 8573, angle 0.796, shotpower 88, > shell-mass 1.8. It packs this information into the buffer provided > (or whatever information is interesting for the server). > (6) The distribution agent encapsulates the data in a message, > addresses the message to shellshock[1].player[1].tankTransport[3] (or > something), and sends it off to the server. > (7) The server locates the Transport called > shellshock[1].player[1].tankTransport[3], and invokes its unpackFrom() > method with the ByteBuffer extracted from the message. > (8) player[1].tankTransport[3].unpackFrom() decodes the buffer and > changes its local copy of tank3 to reflect the firing state. > (9) tank3 on the server sends out a StateChangeEvent which somehow flags > player[2].tankTransport[3] (the proxy for player 2's copy of tank[3]). > (10) The server's distribution agent calls > player[2].tankTransport[3].packTo(), and delivers the state change to > player 2=E2=80=A6 >=20 > Advantages of this design: >=20 > Abstracts the distribution agent from the game specifics >=20 > Potentially allows multiple games to be served by the same > distribution agent (may not really be a good thing=E2=80=A6) >=20 > If we get advanced, allows state updates to be prioritized, so that > more important changes will be delivered first, and less important > changes may be coalesced or dropped under load=E2=80=A6 (See the article= for > details) >=20 > Disadvantages: >=20 > Requires a lot of wire-protocol work on the part of the Transport > developer (although the framework could provide utilities for this) >=20 > Seems to me, user-induced state changes must be applied directly to > client-side objects (see steps 2 and 3 above). To avoid > inconsistency, this means that the client-side objects must have > states that indicate the *intention* of doing something that may or > may not take place. For example, my Tank must enter a "Firing" state > that may, or may not, result in a shot being fired on the server. >=20 > A lot of extra objects floating around! >=20 > Whew! >=20 > Do you guys have any feedback? I'm not attached to this idea, but the > author of the article seems to know what he's talking about=E2=80=A6 >=20 > -j >=20 >=20 >=20 >=20 > On Mon, 21 Jun 2004 16:22:43 -0400, Erich Cranor <3l...@sp...> wrote: > > > > One issue that's going to be coming right up is the details of game sta= te > > communication. > > > > Should remorse be hardwired for 'actors' that have position, movement s= tate, > > etc. Or should remorse have a more flexible approach like a binary xml= that > > allows arbitrary payload? Maybe both eventually. Artillery and combat= will > > use pretty similar state-updates, I would think. But if someone does a= trivia > > game, that'd be rather different. > > > > My suspicion is that it will be well worth our while to be very efficie= nt here. > > If I move my tank, I shouldn't have to explicitly communicate that I'm = not > > moving. So I'm sort of thinking that we need to have operations of th= e form > > 'actor, aspect, change' eg 'tank1, facing, 397' (we're using gradians r= ight?) > > and part of the modules job is to convert between method calls and tran= smission > > modes. > > > > This leads right into 'where exactly is the world state' type questions= . I'm > > still thinking in terms of: > > > > 1. I press spacebar > > 2. Local input module translate this into 'fire' based on my config > > 3. Local game module converts to request 'elc's tank, fire cannon, true= ' > > 4. Local engine sends request to server > > 5. Server dumps request into server's game module > > 6. Server's game module confirms that I'm ready to fire, still alive, a= nd so > > forth, changes my state to firing, adds world state update to some kind= of > > broadcast queue. (also, adds a notice about the existence and position = of a > > tac-nuke shell, but that's another story) > > 7. Server engine sends elc firing update to all players > > 8. Local engine recieves 'elc's tank, firing cannon, true' message and = delivers > > it to local game > > 9. Local game exhibits appropriate beeps and flashes. > > > > That's probably maximal server involvement, but otherwise we enter the = realm of > > jumping backward in time when my code indicated I fired but your code i= ndicated > > your satellite EMP'd me first. > > > > Also, I take it we prefer a mailing list to using the dev forum? > > > > ec > > > > ------------------------------------------------------- > > This SF.Net email sponsored by Black Hat Briefings & Training. > > Attend Black Hat Briefings & Training, Las Vegas July 24-29 - > > digital self defense, top technical experts, no vendor pitches, > > unmatched networking opportunities. Visit www.blackhat.com > > _______________________________________________ > > Remorse-development mailing list > > Rem...@li... > > https://lists.sourceforge.net/lists/listinfo/remorse-development > > > |
From: Jonathan H. <jh...@gm...> - 2004-06-23 17:11:33
|
The Object Views make a lot of sense. But I think I would code your example a little differently. When the fire() command is executed by player 1, an unofficial shell (missile) object would be spawned in the client's game world. This is in addition to adding the TankTransport for the fire change. The player sends the TankTransport, rather than a ShellTransport, because that is the only object that the client may directly invoke for security reasons. When the server unpacks the TankTansport for the Tank's fire(). It would verify that the object executing the request is still in a valid state. This can happen because all the official state and AI logic is on the game server. When unpacking the fire command it spawns a new object, a Shell or missile object. The Shell object would have its own transport. Now when a packTo is executed for Player 2, he receives the ShellTransport and the information needed to indicate its velocity, etc. It would visually appear that Tank 3 fired the shell, but he would never receive a TankTransport for the fire() command. If the tank was turning his turret, this would still be represented using the TankTransport. When the packTo is executed for Player1, he receives the same ShellTransport (or not, if it failed). Player 1 also would recieve acknowledgement that a particular transport packet has been processed. After the player 1 system was done unpacking the transport changes, it would see what unofficial object exist and when they were created. If they were created and there is a package that acknowledges the server side receipt, they would be removed from the world. In this way, the server's shell would be transparently substituted for the unofficial shell. If the command failed on the server the unofficial shell would disappear - for example, if tank 3 was destroyed prior to firing the shell. After the shell has been fired, the impact of side-effects for the shell would be completely handled on the server. A new ShellTransport would update the shell's state on the clients when that occurs. Jonathan PS What is our strategy for synchronizing the time on multiple clients? It seems it important to say, "this was fired at X time". Another question: does this allow for serious game hackers to hack the game? For example by send change events for two seconds in the past, the hacker could sneak in commands that would not normally be processed - for example, firing right before being killed. |
From: Erich C. <3l...@sp...> - 2004-06-23 18:28:40
|
> PS What is our strategy for synchronizing the time on multiple > clients? It seems it important to say, "this was fired at X time". > Another question: does this allow for serious game hackers to hack the > game? For example by send change events for two seconds in the past, > the hacker could sneak in commands that would not normally be > processed - for example, firing right before being killed. Only had time to skim hastily so far, but that timestamp in Josh's example caught my eye too. If we let the player say when something happened, putting aside hacking issues, we invite time-travel responses to lag. I'm wondering if we might be able to say everyone has broadband and let's go for server synching--the firing happens when the server says it happens. Maybe the server doesn't even tick the gameclock until it's gotten command packets (or empty carrier packets) from all players. That makes lag slow everyone down, but with nobody on dialup that might not be a big problem. Testing warranted I suppose (and, of course, I have to go read the article Josh linked to) ec |
From: josh g. <jo...@gm...> - 2004-06-23 21:12:04
|
I should straighten out some misunderstandings about the Object View design pattern. My apologies if I did a poor job of explaining it; I probably should have slept on the email overnight and given it another proofread, rather than sending it at 1:30am. I think I might have even been incorrect about a few details. I'll try again to explain it CONCISELY, and then I'll send another letter discussing the time-travel issue=E2=80=A6 Ok, given Hager's misuse of the lingo, I've decided that "Transport" is a poor name, too=E2=80=A6 I'm going to refer to them as "Transmitters" from now on (although they're really TransmitterReceivers =E2=80=93 they're like two-way radios). Transmitters themselves are not "sent" or "packed," and packTo() isn't really invoked for a player. Instead, maybe a "DistributionAgent.compilePlayerUpdate()" method is invoked for a player, and this method calls several Transmitter.packTo() instances, one for each Transmitter assigned to that player. On the client, each Tank has one TankTransmitter attached to it. On the server, each Tank has several TankTransmitters, one for each client. Each client/server pair of TankTransmitters remembers the state of their object that was *last distributed between them*. Suppose a Tank is changed on a client by user input. When the distribution agent later asks the corresponding TankTransmitter for a message to send to the server (by calling tankTransmitter.packTo()), the Transmitter sees that the Tank's current state is different from what was last distributed, so it constructs a concise message that instructs the server-side TankTransmitter how it should change its server-side Tank to match. When the server-side Tank has been changed, all of the OTHER TankTransmitters for that tank will then be ready to pack up the update for the other clients. So the roles of each of the components involved is pretty simple: The distribution agent keeps a list of Transmitters for each "correspondent." (The client distribution agents each have only one correspondent, which is the server. The server's correspondents are each of the clients.) When the agent wants to send a game-state update to a particular correspondent, it asks each Transmitter for that correspondent to fill a buffer with its state changes. It then addresses each buffer and sends them all to the correspondent. =20 Finally, each buffer is delivered by the remote distribution agent to the correct Transmitter on the other end. Transmitter instances must implement the Transmitter interface, which will include at least these two methods, if not more: // packTo fills the payload and returns true if transmission is required public boolean packTo(Buffer payload);=20 // unpackFrom reads the payload and applies changes to the object public void unpackFrom(Buffer payload); Is that clear now? Hope I'm not beating a dead horse=E2=80=A6 Next up: Time Travel -j On Wed, 23 Jun 2004 10:11:31 -0700, Jonathan Hager <jh...@gm...> wrote= : >=20 > The Object Views make a lot of sense. But I think I would code your > example a little differently. >=20 > When the fire() command is executed by player 1, an unofficial shell > (missile) object would be spawned in the client's game world. This is > in addition to adding the TankTransport for the fire change. The > player sends the TankTransport, rather than a ShellTransport, because > that is the only object that the client may directly invoke for > security reasons. >=20 > When the server unpacks the TankTansport for the Tank's fire(). It > would verify that the object executing the request is still in a valid > state. This can happen because all the official state and AI logic is > on the game server. When unpacking the fire command it spawns a new > object, a Shell or missile object. The Shell object would have its > own transport. >=20 > Now when a packTo is executed for Player 2, he receives the > ShellTransport and the information needed to indicate its velocity, > etc. It would visually appear that Tank 3 fired the shell, but he > would never receive a TankTransport for the fire() command. If the > tank was turning his turret, this would still be represented using the > TankTransport. >=20 > When the packTo is executed for Player1, he receives the same > ShellTransport (or not, if it failed). Player 1 also would recieve > acknowledgement that a particular transport packet has been processed. >=20 > After the player 1 system was done unpacking the transport changes, it > would see what unofficial object exist and when they were created. If > they were created and there is a package that acknowledges the server > side receipt, they would be removed from the world. In this way, the > server's shell would be transparently substituted for the unofficial > shell. If the command failed on the server the unofficial shell would > disappear - for example, if tank 3 was destroyed prior to firing the > shell. >=20 > After the shell has been fired, the impact of side-effects for the > shell would be completely handled on the server. A new ShellTransport > would update the shell's state on the clients when that occurs. >=20 > Jonathan >=20 > PS What is our strategy for synchronizing the time on multiple > clients? It seems it important to say, "this was fired at X time". > Another question: does this allow for serious game hackers to hack the > game? For example by send change events for two seconds in the past, > the hacker could sneak in commands that would not normally be > processed - for example, firing right before being killed. >=20 >=20 >=20 > ------------------------------------------------------- > This SF.Net email sponsored by Black Hat Briefings & Training. > Attend Black Hat Briefings & Training, Las Vegas July 24-29 - > digital self defense, top technical experts, no vendor pitches, > unmatched networking opportunities. Visit www.blackhat.com > _______________________________________________ > Remorse-development mailing list > Rem...@li... > https://lists.sourceforge.net/lists/listinfo/remorse-development > |
From: josh g. <jo...@gm...> - 2004-06-25 18:21:07
|
Whoops... I'm out of town for the weekend. I figure you guys can finish deciding on all of these details by the time I return on Monday... Right? On Wed, 23 Jun 2004 14:11:57 -0700, josh gruenberg <jo...@gm...> wrote= : >=20 > I should straighten out some misunderstandings about the Object View > design pattern. My apologies if I did a poor job of explaining it; I > probably should have slept on the email overnight and given it another > proofread, rather than sending it at 1:30am. I think I might have > even been incorrect about a few details. >=20 > I'll try again to explain it CONCISELY, and then I'll send another > letter discussing the time-travel issue=E2=80=A6 >=20 > Ok, given Hager's misuse of the lingo, I've decided that "Transport" > is a poor name, too=E2=80=A6 I'm going to refer to them as "Transmitters= " > from now on (although they're really TransmitterReceivers =E2=80=93 they'= re > like two-way radios). >=20 > Transmitters themselves are not "sent" or "packed," and packTo() isn't > really invoked for a player. Instead, maybe a > "DistributionAgent.compilePlayerUpdate()" method is invoked for a > player, and this method calls several Transmitter.packTo() instances, > one for each Transmitter assigned to that player. >=20 > On the client, each Tank has one TankTransmitter attached to it. On > the server, each Tank has several TankTransmitters, one for each > client. Each client/server pair of TankTransmitters remembers the > state of their object that was *last distributed between them*. >=20 > Suppose a Tank is changed on a client by user input. When the > distribution agent later asks the corresponding TankTransmitter for a > message to send to the server (by calling tankTransmitter.packTo()), > the Transmitter sees that the Tank's current state is different from > what was last distributed, so it constructs a concise message that > instructs the server-side TankTransmitter how it should change its > server-side Tank to match. >=20 > When the server-side Tank has been changed, all of the OTHER > TankTransmitters for that tank will then be ready to pack up the > update for the other clients. >=20 > So the roles of each of the components involved is pretty simple: >=20 > The distribution agent keeps a list of Transmitters for each > "correspondent." (The client distribution agents each have only one > correspondent, which is the server. The server's correspondents are > each of the clients.) When the agent wants to send a game-state > update to a particular correspondent, it asks each Transmitter for > that correspondent to fill a buffer with its state changes. It then > addresses each buffer and sends them all to the correspondent. > Finally, each buffer is delivered by the remote distribution agent to > the correct Transmitter on the other end. >=20 > Transmitter instances must implement the Transmitter interface, which > will include at least these two methods, if not more: >=20 > // packTo fills the payload and returns true if transmission is required > public boolean packTo(Buffer payload); >=20 > // unpackFrom reads the payload and applies changes to the object > public void unpackFrom(Buffer payload); >=20 > Is that clear now? Hope I'm not beating a dead horse=E2=80=A6 >=20 > Next up: Time Travel >=20 > -j >=20 >=20 >=20 >=20 > On Wed, 23 Jun 2004 10:11:31 -0700, Jonathan Hager <jh...@gm...> wro= te: > > > > The Object Views make a lot of sense. But I think I would code your > > example a little differently. > > > > When the fire() command is executed by player 1, an unofficial shell > > (missile) object would be spawned in the client's game world. This is > > in addition to adding the TankTransport for the fire change. The > > player sends the TankTransport, rather than a ShellTransport, because > > that is the only object that the client may directly invoke for > > security reasons. > > > > When the server unpacks the TankTansport for the Tank's fire(). It > > would verify that the object executing the request is still in a valid > > state. This can happen because all the official state and AI logic is > > on the game server. When unpacking the fire command it spawns a new > > object, a Shell or missile object. The Shell object would have its > > own transport. > > > > Now when a packTo is executed for Player 2, he receives the > > ShellTransport and the information needed to indicate its velocity, > > etc. It would visually appear that Tank 3 fired the shell, but he > > would never receive a TankTransport for the fire() command. If the > > tank was turning his turret, this would still be represented using the > > TankTransport. > > > > When the packTo is executed for Player1, he receives the same > > ShellTransport (or not, if it failed). Player 1 also would recieve > > acknowledgement that a particular transport packet has been processed. > > > > After the player 1 system was done unpacking the transport changes, it > > would see what unofficial object exist and when they were created. If > > they were created and there is a package that acknowledges the server > > side receipt, they would be removed from the world. In this way, the > > server's shell would be transparently substituted for the unofficial > > shell. If the command failed on the server the unofficial shell would > > disappear - for example, if tank 3 was destroyed prior to firing the > > shell. > > > > After the shell has been fired, the impact of side-effects for the > > shell would be completely handled on the server. A new ShellTransport > > would update the shell's state on the clients when that occurs. > > > > Jonathan > > > > PS What is our strategy for synchronizing the time on multiple > > clients? It seems it important to say, "this was fired at X time". > > Another question: does this allow for serious game hackers to hack the > > game? For example by send change events for two seconds in the past, > > the hacker could sneak in commands that would not normally be > > processed - for example, firing right before being killed. > > > > > > > > ------------------------------------------------------- > > This SF.Net email sponsored by Black Hat Briefings & Training. > > Attend Black Hat Briefings & Training, Las Vegas July 24-29 - > > digital self defense, top technical experts, no vendor pitches, > > unmatched networking opportunities. Visit www.blackhat.com > > _______________________________________________ > > Remorse-development mailing list > > Rem...@li... > > https://lists.sourceforge.net/lists/listinfo/remorse-development > > > |