Jump to content

Rick

Members
  • Posts

    7,936
  • Joined

  • Last visited

Everything posted by Rick

  1. Rick

    Unit Testing

    I agree with the inconsistent context. There seems to just be way to many static things in LE3. If you wanted to do that just make it a C library like LE2 then.
  2. The only thing Valve cares about in all of this is to stop PC OS marketplaces in their tracks because if we can sell our games through a Windows OS marketplace there is no need for Valve anymore. The alternative WAS extremely painful, and some can still say somewhat painful still when compared to the ease of use and massive feature set in .NET.
  3. When you run from the editor it runs a game script. I set a global var in that script to let me know if in game mode or not.
  4. Rick

    the stack

    In Visual Studio the call stack shows you the chain of functions. If you dbl click it'll bring you to said function (normally look for the last of your functions and go there). Then you can mouse over variables to see what's up. Generally this is all I need to tell me why I got the error.
  5. So you want to only send some network traffice once a min? Think if it took a minute before others see your chat messages. Or a minute to tell you that the other player actually picked up this item that you thought you picked up because you both got there at at the same time. As a player I'd be pretty pissed to think I have an item for 60 seconds before the game takes it back away from me because it finally realized I didn't really get there first to get it. The truth is almost nothing you do in a networked game will ever be spaced out that long. Prediction only masks the reality for a very short time to help the illusion. Letting corrections only happen every 60 seconds would cause all sorts of visible problems in games. I could do all sorts of cheats on the client within that 1 minute time window. 1 min is a very long time in the world of networking considering things generally happen around 100ms or so. The 2 main things you are thinking about with online games: 1) Create the illusion of instant 2) Stop the cheating These 2 things are the most important. If your game fails on either of these 2 the game will die quickly. Nobody likes lag and a sluggish game, and when your game is filled with cheaters nobody likes that either.
  6. Just some code I've used/read before. If you use game states and state manager this can help simplify things. This is what I use myself. I've done this from memory so it's just to get the idea behind it and not 100% how mine looks. void StateManager::NetworkUpdate() { for(p=client->Receive(); p; client->DeallocatePacket(p), p=client->Receive()) { // this reads the first byte of the packet and is the enum values like ID_REQUEST_LOGIN packetIdentifier = GetPacketIdentifier(p); // get the bitstream to read the data and ignore the message type so we can just read the game data right away when inside // our function RakNet::BitStream reader(p->data, p->length, false); reader->IgnoreBytes(sizeof(MessageID)); // validate our current state has registered/subscribed to this network message if(_currentState->IsNetworkMessageRegistered(packetIdentifier)) { // if it has, fire the message off _currentState->FireNetworkEvent(packetIdentifier, p, reader) } } } // inside base State class map<MessageID, Event2<Packet*, BitStream&>> _networkMessages; bool State::IsNetworkMessageRegistered(MessageID msg) { if(_networkMessages.find(msg) != _networkMessages.end()) return true; return false; } void State::FireNetworkEvent(MessageID msg, Packet* p, BitStream& reader) { // this will fire the function that was bound to the message _networkMessages[msg].Raise(p, reader); } void GameplayState::OnEnter() { // this replaces the need for a big ugly switch state and is how you link network messages to member functions _networkMessages[iD_RESPONSE_LOGIN].Bind(this, &GameplayState::OnResponseLogin); } void GameplayState::OnResponseLogin(Packet* p, BitStream& reader) { RakString status; RakString reason; reader.Read(status); if(status == "failure") { reason = reader.Read(reason); printf("Failed to login: %s", reason.C_Str()); } } This is the way I do things. A slight modification to this could be to make classes for each message type and a factory create said type using the message id. void Gameplay::OnResponseLogin(LoginMessage& msg) { if(msg.Status == "failure") printf("Failed to login: %s", msg.Reason); } This is "cleaner", but it adds more overhead to make work. With this you need a class for every message id and you'll end up with hundreds of small classes. It also requires some casting being done to the message class type. The nice thing about this is that inside the network function you don't have to worry about reading the data as that's been done for you in the message class itself. However, you'd still have to do that reading in the class, but if working in a team project it can lead to nice separation of duties. These are just things I've done/learned from others and is no way the only way to do things, but just thought I'd share my experiences.
  7. Something to watch out for, if I'm not mistaken that code will run in game also. When I was doing stuff like this with my Pi scripts, I made a flag that would tell me if I'm in the editor or running the game so I could skip over editor parts when running the game.
  8. Also something to note is that as your coding many parts of network games you notice that you end up doing very similar things on both the client and server. There is room for sharing source files between them with possibly some #ifdef stuff in the code to branch on compile if it's client vs server inside said functions. When I first looked at HL source I noticed Valve did this all over the place, and as I started doing network programming myself I saw why. No sense in duplicating code if only 10% of a class is different because it runs on the server vs client. This can save a lot of time and bugs. You can notice this in the example code as well. ID_SPAWN _PLAYER looks very similar on client and server. They both require a Player class, but the insides can be slightly different based on client/server but no real need to make 2 Player classes, one in the server project or 1 in the client project. Just make a separate area for shared classes and include those files in both client & server projects with in #indef checks if you are client or server and then branch off the differences. Since you almost always want the client and server doing the exact same checks for things, this means you only have to code those checks once instead of twice, or copy/paste.
  9. Like gamecreator says the code is lengthy and not very friendly to break out specific problem areas. Having the code directly in switch statements and even having a switch statement at all is probably not the ideal method for this as you can see it can get very lengthy and hard to read and separate out. You see common things (like ignoring the message byte) in each message. This should be done at a higher level since it needs to be done for all messages. Ideally you would have 1 function for each message type so you can break things out easier and if you like you could even create a Message base type that packages up your network messages into actual types so that those functions that process them take said type that has specific fields for that message. This way you can easily break things out, check that all the data that was expected is there, and see the data easier. If I were you, I'd start small with RakNet and figure out a nice way to organize things. The switch statement is just an example RakNet shows in their tutorials but it's far from ideal in a real situation as you can end up with hundreds of messages and things will get unruly very quickly. My messages generally have a REQUEST/RESPONSE theme to them. The client generally sends REQUEST type messages and the server generally sends RESPONSE message. ie ID_REQUEST_LOGIN, ID_RESPONSE_LOGIN. The way I map messages to class functions is with the event class I have posted here http://www.leadwerks.com/werkspace/files/file/367-c-event/. Then I have a map where the key is the network message and the value is the event type. I usually use the event that takes 2 parameters. The first being the Packet*, the second being the BitStream. Then when I get a packet, I use the first byte (which is the message) and see if it exists in the map. If so, I raise the event, which calls the function for that event. I find this creates a nice separation of messages and makes things easier to understand myself.
  10. The player on the server is very necessary, but it just doesn't require the visuals. You will want a "player" both locally with visuals and on the server without (in LE2 you could just have the controller for the physics). Replicating objects from server to client is a fundamental thing in networking. You will want to do that. I didn't look through your code, but what is the link that you have between characters on client and server? Usually there is some kind of ID that links a client to it's replicated counterpart on the server.
  11. Yeah, this plays into some of the predictions that speed up the client. You are predicting on the client that this action is OK and really did happen because you ran the same rules that the server will run. If the server agrees with your client nothing has to be changed, but if the server doesn't agree with your client it'll send a correction. This generally all takes a very short amount of time so it's hard to notice the correction if one needs to happen. In fast paced games if 2 people are going for the same ammo pack and the game has it being picked up on collision instead of doing some kind of animation for picking it up, sometimes you might hear a pickup sound on your end but don't see the ammo added (it probably added it to the ammo count text, but within the 50 ms or so to correct it, our brains wouldn't notice the small flicker of correction to the text. Also, in a fast paced game how often are you staring at your ammo count anyway really.). The odds that both players collide within the 25-100 MS that most pings are these days for these games is rare. In a game I have when the player right clicks on a resource I play a little growing bubble animation, and when it's done the options for that resource come up. Sort of like how The Sims does their actions. Because I want full control of changing these at any time, during this 300ms animation of the bubble growing, I request from the server all actions I can do on this resource. I then display those when the animation is finished. However, I have to account for both ways. The animation may finish before I get a response from the server with lots of lag. So I set flags on both sides. One for the animation and one for the server response. Then in both areas I check if the other flag is true and if so then I setup my action option buttons for display. So there are just really different ways to think about things when adding network code. It's fun and annoying at the same time lol
  12. Your server generally doesn't have any graphics or even require a graphics card for that matter. However, if you are using LE library on the server side for it's various functionality, then with LE2 you require a decent graphics card to even run your program. This is LE's "fault" though as it doesn't have a feature to run otherwise. The server generally is the truth of everything. It validates and controls everything. The client generally does similar things as the server as in validation but it's not trusted at all. It only does validation and such to give an instant response but can be overruled by the server on everything. So if your character collides with an ammo pickup, locally on the client you can check that collision and instantly give the player the ammo locally, but the server is doing the same checks and will also give the player the ammo on the server and send that it did so or not to the client, and the client will always listen to the server. If you didn't do something like this and the player has a ping of say 250 ms, the delay will be noticeable on the client side and "feel" laggy. You don't want that. You want the client to always feel fast and crisp! You can do that via just doing the action on the client but at the same time sending requests to the server to make sure it's OK to do it, and when getting the response from the server act accordingly OR you can hide the lag in other ways. In some RTS games when you select a unit and tell them to go to a certain place, you might notice they do a little salute animation and say something like "Yes, sir!". This all might take 300ms or so and seem natural on your client, but what it's doing behind the scenes is sending a request to the server, getting a response, then doing the action. Why do you have to do this? Because the client can be hacked. In the above example what if I requested to move into some trees that normally the collisions would not let me do this, but because I hacked the client I was able to skip those collision checks and do it anyway. If the server didn't get involved I could now hide in tree areas that normally I wouldn't. If it was a ranged unit I could potentially be invincible then as other units couldn't get to me if they didn't cheat. With the server it'll send back the response as NO, you can't. Now your local client could be hacked to ignore that response and do it anyway, that would only be happening on YOUR client and not on the server or anyone else's client, which means for everyone else you'd still be where you were before you made the move, and an easy target. As Ken said above, smoke and mirrors. There are many illusions/tricks that happen in an online game.
  13. Rick

    It's Friday

    My dream is to have a whiteboard that is the entire size of a wall because it'll make me feel important and cool
  14. Rick

    Timers

    #pragma once #include <windows.h> #include "Event.h" class Timer { private: float _interval; unsigned __int64 _lastInterval; double _freq; bool _enabled; public: Timer(float interval); ~Timer(void); Event0 OnTick; void SetInterval(float interval); float GetInterval(); void Enable(); void Disable(); bool IsEnabled(); void Update(); }; #include "Timer.h" Timer::Timer(float interval) { _enabled = false; _interval = interval; unsigned __int64 pf; QueryPerformanceFrequency((LARGE_INTEGER*)&pf); _freq = 1.0 / (double)pf; } Timer::~Timer(void) { } void Timer::Disable() { _enabled = false; } void Timer::Enable() { _enabled = true; QueryPerformanceCounter((LARGE_INTEGER*)&_lastInterval); } void Timer::SetInterval(float interval) { _interval = interval; } float Timer::GetInterval() { return _interval; } bool Timer::IsEnabled() { return _enabled; } void Timer::Update() { if(_enabled) { unsigned __int64 temp; QueryPerformanceCounter((LARGE_INTEGER*)&temp); // get the time in ms float lastTime = ((temp - _lastInterval) * _freq) * 1000.0f; if(lastTime > _interval) { // fire the event OnTick.Raise(); _lastInterval = temp; } } } Get Event.h here http://www.leadwerks...le/367-c-event/ Usage would be: class MyClass { private: Timer* tmrTest; public: // this will be our callback void tmrTest_OnTick(Timer* sender) { } MyClass(){ tmrTest = new Timer(5000); // 5 seconds // bind the timer on tick event to a class method tmrTest.OnTick.Bind(this, &MyClass::tmrTest_OnTick); } }; Be sure to call tmrTest->Update() each frame
  15. Rick

    One Last Thing

    Ironically enough I think you said the same thing about pathfinding code about 3 years ago. Just saying, times change and what is "standard" changes with it.
  16. Didn't Metatron already beat you to this?
  17. A good number of sql engines have a concept they call prepared statements. This allows you to define parameters and set those parameters with values. The reason you want to use these instead of building the sql string yourself is to avoid something they call sql injection. Here is an example on how to do it using Lua: http://www.nessie.de/mroth/lua-sqlite3/documentation.html#ref11 . This is the preferred method. You also get a speed boost by using prepared statements.
  18. This seems to be exactly what you are looking for. Instead of storing the function pointer in your button class Just store an Event0 object. Then bind your function in your other class to that. In order to call class methods you have to store a pointer to that class as well as the function pointer. This is what the Event classes I linked to is doing. Here is an example on how to do it but you'll notice it's very specific to the 1 class. I assume you want this to be generic enough to handle member functions from any class, and that's what the code I linked to above is doing. http://publib.boulde...ref/cplr034.htm An "good" event system is one that allows calling member functions to other classes. Seems to be the same thing you are asking for here. They are one in the same. Function pointers to class methods has to be treated differently than normal stand alone. You must have a pointer to the class in order to make the call. Again, I think you'll find what I linked to does what you want very easily and is very flexible to work between any classes and wraps it all up in a nice little class to avoid dealing with function pointers.
  19. http://www.leadwerks.com/werkspace/files/file/367-c-event/ Does nobody look at the assets store
  20. Yeah, this event code really opens up doors when coding in C++. I use this all over the place. I didn't create this code, someone taught me a long time ago about it, but it's amazing and gives C++ a really nice event system. Hope it opens up a whole new world for you because it did to me
  21. You might want to take a look at http://www.leadwerks...le/367-c-event/ Otherwise it looks like you have a C++ library but your event callbacks are normal C functions, which makes it awkward for people using your library. I mean if I'm using a C++ library I expect everything about it to be object oriented myself. That includes the event callbacks being able to be class methods and not normal C functions.
  22. Rick

    Mages Alchemy

    Commenting as I'm playing. Take comments for what they are worth - Create New User screen: - Would like to use the keyboard to type my name - The on screen keyboard is in alphabetical order and not a "normal" keyboard layout. - Loading screen: - The image black wasn't the same as the bg black. I'm guessing you want that to blend so we can't really see the bounds of the image? - Game: - Animation looks pretty choppy. Are you doing animation blending? That would help smooth this out. - If I jump and move at the same time there is a good amount of gliding the character does. - There is a good amount of gliding happening - Camera angle gets to far away sometimes. After the first bridge I'm in the middle of the bridge and running over the coins was a little odd. - In 1 bridge lowering I got 2 of the exact same formulas. Maybe make it so that can't happen. - Not sure what the coins are for. They don't seem to serve any real purpose - Takes way to long to tally the scores. I feel these should go faster and more flash and bang. - Starting level 2, maybe just automatically give me the mana and down spells from last level. Not much sense in just easily picking them up when I already know them. - More variety in puzzles. (I know you have this planned) 3rd level froze on the loading screen. Not sure what happened but I just killed it at that point. Love the music, love the scenery, great UI & overall game look and feel. I think the puzzle variety would go a long way with a game like this. Keep up the good work!
  23. Yeah, that's another way to do it. I just prefer having 1 callback function that automatically handles all my collision forwarding to my objects.
×
×
  • Create New...