Jump to content

  • Log In with Google      Sign In   
  • Create Account

rip-off

Member Since 16 Mar 2005
Online Last Active Today, 04:27 AM

#4928772 Client-Server Game Logic Distribution

Posted by on 06 April 2012 - 07:17 AM

The problem with simulating stuff on the client is that the client now has a role in determining the outcome. The client also has an incentive to influence this outcome in it's favour, i.e. to cheat. So far, no-one has been able to solve the problem of preventing modified clients (or proxies) from interacting with their servers, so you'll have to do some cutting edge research or accept that potentially malicious (or even just buggy) clients can be used.

Almost every game allows some scope for cheating. Generally, in order to simulate the game without excessive latency, the game client is almost always aware of "additional information" that the player shouldn't have. A simple example is that in FPS games, the client must be aware of the rough position of combatants, even if they are out of sight, in order to play sounds for them. A modified game client could render this combatant, making it considerably easier for the player to locate and kill them (a wall hack, for example).

Given that almost no game can perfectly prevent all possible types of cheating, the question becomes: how much cheating are you prepared to allow? When I say "allow", I just mean that the game itself cannot prevent the cheating. You can provide additional tools to allow other players deal with suspicious activity, such as a vote kick system.

I assume you want to minimise cheating. The simplest solution is to do all decisions on the server.

Let us go back to the start. I infer from your post that you believe such a game will be complex enough* that you must have the clients do some of the work. I think that this is an untested assertion. I think your first step should be to outline the processing, bandwidth and latency requirements of a purely authoritative server. If this exceeds some reasonable limit, only then should you think about how to offload decisions to the client.

By understanding the nature of the server's workload and bandwidth/latency, you can identify the areas that you can offload that will have the biggest impact on performance while minimising the scope for cheating.

You could take other approaches too. A lockstep simulation might be possible. This would more or less prevent cheating, because the cheater's simulation will get out sync. The bandwidth and latency requirements are quite different here.

You could have a peer to peer game that isn't in lockstep. The problems that arise here, cheating aside, is determining which client is authorised to determine the outcome of an action, or whether you can merge two slightly inconsistent views on the game somehow.

* I haven't much Diablo, and I have never played it online


#4928705 rename variables in release mode (like C++ #define)

Posted by on 06 April 2012 - 01:58 AM

I'd like to change variable names from debug to release mode.

Why?


#4928467 Little help with throwing exceptions (custom ones).

Posted by on 05 April 2012 - 07:31 AM

I believe you can download pre-built versions of Boost. Read Boost's Getting Started on Windows guide.

As BitMaster says, due to heavy use of templates, many Boost libraries are "header only". You don't need to build boost to use them. As such, all you need to do is merely add the Boost include directory to your project path, and you should be able to get lexical_cast<>.


#4927832 Include problems

Posted by on 03 April 2012 - 04:06 AM

For the sake of this analysis, I'm going to assume that the contents of "globals.h" does not depend on any of your object classes.

The standard solution to this is as follows:

globals.h
#ifndef GLOBALS_H
#define GLOBALS_H

void someFunction(/* ... */);

#endif

globals.cpp
#include "globals.h"

void someFunction(/* ... */) {
    // ...
}

object.h
#ifndef OBJECT_H
#define OBJECT_H

class Object
{
public:
    virtual ~Object();
    virtual void frobnicate() = 0;
    // ...
};

#endif

object.cpp
#include "object.h"

Object::~Object()
{
}

bullet.h
#ifndef BULLET_H
#define BULLET_H

#include "object.h"

class Bullet : public Object
{
public:
    virtual void frobnicate();
};

#endif

bullet.cpp
include "bullet.h"

#include "globals.h"

void Bullet::frobnicate()
{
    // ...
    someFunction(/* ... *);
    // ...
}

main.cpp
#include "bullet.h"

int main()
{
    Bullet bullet;
    bullet.frobnicate();
}
Now, this is just the structural overview. We see that the general structure of a header file is that it has include guards, which should be directly related to the file name to avoid collisions. There is no need to use lots of underscores, a simple _H suffix will ensure that it doesn't collide with sane macros. If you wish, you can use #pragma once instead of, or in addition to, include guards.

The next thing to note is that, in general, we do not put implementations in the header files. This is possibly what is causing the "multiple definitions" error you alluded to (I will talk about this in a moment).

Finally, each header file only #includes files it actually depends on. In some cases, a forward declaration will suffice, but for inheritance you need a full class definition. You'll note I put the #include "globals.h" inside bullet.cpp, rather than in bullet.h.

Also note that each source file includes its header file as the very first line. This is very important, because otherwise you can get very brittle header files that depend on the order of inclusion. What you want is for each of your header files to compile correctly in isolation (as I mention below, header files aren't really compiled themselves).

oh i almost forgot i dont know if this will be relevant but my globals.h has both declaration and definitions should i seperate them into a .h and a .cpp?

Header files should not contain variable or function definitions. No amount of include guards or pragma once can defend against this. It is critical to understand the C++ compilation process, which I will call the "build" process here to avoid the confusion between the overall process (called compiling) and one particular stage in that process (also called compiling).

The basic overview is that we have three stages, preprocessing, compiling and linking. The output of each stage is fed to the next. An important point is that only "source files" are built. That is, files that end in .c or .cpp. Headers are only built by including them in one or more source files.

The pre-processing stage is the simplest. Each source file is fed into the pre-processor independently. All the #includes, #defines and #if...#endif and #pragmas are evaluated. Note that #include here is a simple copy/paste operation, the pre-processor literally pretends you wrote all the code in that file.

It is critical to note that the pre-processor doesn't understand C++, it only understands #include, #define etc. If the result of the pre-processing stage is total gibberish, then this is what the compiler stage will see, and complain about. Certain classes of compile errors are hard to diagnose because information can be essentially "lost in translation".

The output of of the pre-processing stage is called a "translation unit".

Each translation unit is then compiled in isolation. Here, the compiler parses the c++, tries to understand it, makes sure you obey all the rules, and finally outputs a "object file". The terminology "object file" pre-dates C++, and has nothing to do with object oriented programming or classes. The terminology really refers to data and function definitions as "objects". On Windows systems, object files will be something like main.obj or bullets.obj. On Unix derivatives, these would be called main.o or bullets.o.

The inner details of the compilation stage is a complex but interesting process, much more could be written about it, which I will not do so here. This is just an overview.

Finally, we have a bunch of object files, one for each source file. The link stage tries to put them together to form an executable. Here it must resolve references to data and functions, i.e. the "objects" referred to earlier. If all goes according to plan, the linker will output an executable file. Roughly speaking, object files list the references they need and the references they require. For example, bullet.o says "I provide Bullet::frobnicate, but I require someFunction", object.o says "I provide Object::~Object", globals.o says "I provide someFunction". main.o says "I require Bullet::frobnicate, Object::~Object, I provide the entry point (main)" (again, a simplification - there are other symbols required for virtual functions, etc).

This is the build process. If you are interested, you can force your toolchain to output the various intermediate files (object files are typically output by default, to enable incremental builds).

Now that you hopefully have an idea of what is going on, we can track down this error. What happens if you have a "globals.h" file that contains function definitions? Well, if it is included only once, it will end up in exactly one translation unit, and in one object file. When the linker links this with the other object files, it will not find any name collisions and all will work.

However, if two (or more) different source files end up including such a header (directly or indirectly), then the function bodies will end up in two translation units. These are compiled in isolation, so we will get two object files which both say "I have an implementation of someFunction". Linkers are pretty stupid, if the linker finds multiple pieces of data or function bodies with the same name it will complain, even if they are identical!

Also to go back to the earlier point about trying to write header files that would compile in isolation. You can see now the difficulty - header files aren't compiled, except when included. By including them as the first line in a source file, we ensure that this header file would compile if it were actually compiled itself. Well, almost. It is possible to do silly things that will break this assumption (a simple example is to omit the closing semi-colon on the class body, and include it in the second line of the associated source file).


#4925632 Struggling with an items database

Posted by on 27 March 2012 - 05:23 AM

Despite both being weapons, if the behaviour is sufficiently different think hard before trying to shoehorn them into the same container.

If you end up promoting so much of the interface to the parent, then you haven't gained much at all. At this point, you might consider making a single Weapon class, and making Melee weapons acting like a ranged weapon but having a short range and infinite ammunition (that is, turning the behavioural differences into data). This might be preferable to implementing the object specific logic in the class that uses the object.


#4924354 Java or C++?

Posted by on 22 March 2012 - 09:49 AM

But I guess I can provide my own point of view:

This is For Beginners. Try to stick with facts, rather than opinions.

Just to quickly refute some of your points:

C++ is hardest out of those 3 because it has memory management, pointers and other weird stuff, however it has high performance because it's not virtual machine like Java, and doesn't require dynamic linking for everything like in C#. Therefore it's best suited for demanding applications (database, AAA games, simulations, etc).

Virtual machines have nothing to do with performance, nor does dynamic linking. The fact that current implementations of these might incur performance overhead is not a particularly interesting argument.

Consider virtual machines, if you look back a few years and see the performance difference between what they were then and where they are now - you will see that most of the "performance issues" are actually implementation issues. In addition, language implementations that have some kind of hypervisor/compiler functionality have lots of opportunities for advanced optimisation that we're only scratching the surface of (e.g. V8 javascript engine compiling dynamic function calls to inline assembler - no current C++ compiler can do this).

As for dynamic linking performance, I imagine any performance issues are most likely related to Microsoft attempting to keep backwards compatibility with their existing DLL systems. I've never looked into C#'s dynamic linking, so I cannot comment for certain, but if you were free to design a new dynamic link system from scratch, I see no reason one would have to pay a heavy runtime price for it. Indeed, again the nature of these languages could serve as a benefit, the abstract instructions here could be fully inlined into the caller when they are dynamically linked (possibly but considerably more difficult for fully native code).

Java is very cross-platform therefore is best suited for applications that target wide audience, however it's quite big letdown when it comes to performance. Huge amount of packages allows rapid development as well, but inability to create overloaded pointers can make game development complicated because of heavy math involvement.

Overloaded pointers?

Java's main performance pitfall is the memory structure - it suffers because it doesn't allow explicit value objects and AFAIK little or no memory layout optimisations are done automatically. This is where 95%+ of modern performance issues are - memory layout problems causing the busy loops to fall outside the cache.

Also I don't consider Java and C# true programming languages because they cannot run on machine without OS/extra libraries, like Assembly/C/C++ can...

Standard C++ and C cannot run on their own without an OS. Arguably, no program can run without an OS, without also being an OS.

In any case, C and C++ operate in their own kind of "virtual machine", just one that happens to map more or less directly to hardware very often. This abstraction insulates you from the processor just enough that you would need some assembly or language extensions to bootstrap you and/or to accomplish certain tasks.

And of course if you allow C or C++ that, then surely you must allow Java, C# or even Javascript the same, and you are back to where you started.

However unless you are in the business of writing operating systems I don't see any relevance of this line of reasoning.

... therefore I consider both of them to be scripting languages.

Arbitrary, line in the sand nonsense. The classification of a programming language as a "scripting language" (the latter usually considered a subset of the former, not a disjoin set), even under a definition of scripting language as unusual as yours (typically scripting language is more or less synonymous with dynamic typing), is irrelevant.


#4923974 Name Conflict?

Posted by on 21 March 2012 - 10:24 AM

The following compiles for me (on GCC)
template<typename T>
class TArray
{
public:
    const T* GetArray () const;
    T* GetArray ();
private:
    T *m_atArray;
};

template <class T>
T* TArray<T>::GetArray ()
{
     return m_atArray;
}


template <class T>
const T* TArray<T>::GetArray () const
{
    return m_atArray;
}

int main()
{
    TArray<int> a;
    a.GetArray();
    const TArray<int> &c = a;
    c.GetArray();
}
Can you produce a minimal example that demonstrates this error?


#4923621 [C++] Help me with basic organism simulation

Posted by on 20 March 2012 - 08:19 AM

Your vector stores pointers, so you'd have to pass the address of a ClassA object. For instance:

int main(void)
{
         ClassA classAObj;
         vector < ClassA* > myvector;
         myvector.push_back( &classAObj );
         myvector[ 0 ] -> function_a();
         return 0;
}
However, storing raw pointers in a container is an advanced technique, it is easy to cause undefined behaviour unless you know how to correctly manage pointers. The code above stores a pointer to a local object in the container. If the container outlives the local (it doesn't in that last example) then you could easily corrupt memory by writing to data members of the pointer.

Looking at your original post, once we remove the unnecessary child class your classes can be treated as "value objects". That is they have simple copying semantics. So I would store them in containers by value:
std::vector<Food> food;
std::vector<Organism> colony;

food.push_back(Food());
colony.push_back(Organism());
colony.push_back(Organism());



#4923078 Input for a windows game

Posted by on 18 March 2012 - 11:34 AM

Raw Input is not necessary for this, unless perhaps if you need high resolution mouse input. You can use standard Windows input to implement an XNA-style interface.

For the former, use GetKeyboardState. For the latter, you can use GetAsyncKeyState (yes - it works with mouse buttons) and GetCursorPos() and ScreenToClient() to get the mouse position.

Alternatively just handle regular input events in your message loop.

Caveat: I am not a Win32 programmer.


#4922436 SDL player.update doesnt update?

Posted by on 15 March 2012 - 06:08 PM

Well you don't clear the screen, so anything you have previously drawn will still be underneath your new stuff. A simple way to clear the screen is use the SDL_FillRect() function, and pass a NULL SDL_Rect pointer:
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));



#4922240 SDL player.update doesnt update?

Posted by on 15 March 2012 - 06:09 AM

You have two poll event loops, but SDL only has a single event queue. The event loop in main will pick up the first even that occurs in a given frame, and the player::update() will pick up the second. They will then alternate while events are still in the queue (for non-mouse games, there are generally few events in the queue).

I recommend having a single even loop, and passing interesting events to the player object:
// In player.h
class player {
public:
    // ...
    void update(SDL_Event event);
    // ...
};

// Inside main()


while (SDL_PollEvent( &event))
{
    if (event.type == SDL_QUIT)
    {
        quit = true;
    }
    else
    {
        gamePlayer.update(event);
    }
}
Now, in this case your player object will correctly receive the events of interest.

There may remain another issue however, depending on the style of your game. If your game expects the player to continue moving while the key is held down, then you need a way to "remember" the direction of movement and a way to update the position even when events do occur.

A simple solution is to add some variables to the player to indicate the current direction and speed of movement, and to separate the event code from the update code, like so:
class Player
{
public:
   Player();

   void update();
   void onEvent(const SDL_Event &event);

    // ...

private:

    int x;
    int y;
    int vx;
    int vy;
};

// Player.cpp

Player::Player()
{
   x = 100;
   y = 100;
   vx = 0;
   vy = 0;
}


void Player::onEvent(const SDL_Event &event)
{

    const int SPEED = 10;
    if (event.type == SDL_KEYDOWN)
    {
        switch(event.key.keysym.sym)
        {
        case SDLK_UP:    vy -= SPEED; break;
        case SDLK_DOWN:  vy += SPEED; break;
        case SDLK_LEFT:  vx -= SPEED; break;
        case SDLK_RIGHT: vx += SPEED; break;
        }
    }
    else if(event.type == SDL_KEYUP)
    {
        switch(event.key.keysym.sym)
        {
        case SDLK_UP:    vy += SPEED; break;
        case SDLK_DOWN:  vy -= SPEED; break;
        case SDLK_LEFT:  vx += SPEED; break;
        case SDLK_RIGHT: vx -= SPEED; break;
        }
    }
}

void Player::update()
{
    x += vx;
    y += vy;

    // Maybe some code to keep the player inside the screen/world/map bounds?
    SDL_Surface *screen = SDL_GetVideoSurface();

    if(x < 0)
    {
        x = 0;
    }
    else if(x > screen->w)
    {
        x = screen->w;
    }

    if(y < 0)
    {
        y = 0;
    }
    else if(x > screen->h)
    {
        y = screen->h;
    }
}
An interesting "trick" here is starting the velocity at 0, and adding or subtracting the speed to it, rather than simply setting the speed. You may wonder at this idea - surely any key can only be pressed down once, and thus the difference would appear irrelevant.

For reference, here is the type of implementation I'll be discussing:

void Player::onEvent(const SDL_Event &event)
{
    const int SPEED = 10;
    if (event.type == SDL_KEYDOWN)
    {
        switch(event.key.keysym.sym)
        {
        case SDLK_UP:    vy = -SPEED; break;
        case SDLK_DOWN:  vy = SPEED; break;
        case SDLK_LEFT:  vx = -SPEED; break;
        case SDLK_RIGHT: vx = SPEED; break;
        }
    }
    else if(event.type == SDL_KEYUP)
    {
        switch(event.key.keysym.sym)
        {
        case SDLK_UP:    vy = 0; break;
        case SDLK_DOWN:  vy = 0; break;
        case SDLK_LEFT:  vx = 0; break;
        case SDLK_RIGHT: vx = 0; break;
        }
    }
}
Imagine that the player starts by holding down the UP key. So their y velocity is incremented to -10. Now, imagine that without releasing the UP key they press the down key. If you set the velocities, rather than added to them, then their velocity would become +10. With the addition method, it becomes 0 - which I think is reasonable if the user holds the up and down keys at the same time.

However, even that, while nice, is not the real "win". Think what happens now if the user releases the either key under the "set" implementation - the velocity becomes zero. But the player is still holding down a key!

This isn't perfect. If you are running the game in a window, and the game loses focus, the state of the player can get out of sync with the keys. Another option is simply to ask for the current key "state":

class Player
{
public:
    Player();

    void update();
    // ...

private:
    int x;
    int y;
    // ...
};

Player::Player()
{
    x = 100;
    y = 100;
}

void Player::update()
{
    const int SPEED = 10;
    Uint8 *keys = SDL_GetKeyState(NULL);

    if(keys[SDLK_UP])
    {
        y -= SPEED;
    }
    if(keys[SDLK_DOWN])
    {
        y += SPEED;
    }

    if(keys[SDLK_LEFT])
    {
        x -= SPEED;
    }
    if(keys[SDLK_RIGHT])
    {
        x += SPEED;
    }

    // Bounds code is similar
}
In this case, you would have no player related code in your event loop, instead you must call Player::update() every frame.

Finally, note that I'm using regular integers to hold the player's position, rather than a SDL_Rect. SDL_BlitSurface() modifies the "destination" rectangle passed to it to contain the "clipped" co-ordinates. You are passing a copy, so this doesn't affect you... yet.


#4921789 Whats the worst a bad pointer could do

Posted by on 13 March 2012 - 04:18 PM

Remember, the user the process is running as can still access the disk. Malicious exploits aside, your program could still end up corrupting random files if you were very unlucky.


#4920453 Replace part of string in C++

Posted by on 08 March 2012 - 11:30 AM

Google will tell you what -Wall means. You'll probably need to search for something like "gcc wall", the minus symbol actually inverts a search term (you'll get all the pages except those that mention "wall").

You should also include -Werror in your command line options.

As for that code, be more specific. Include the actual source and actual compiler message. Here is a minimal program:

#include <string>
#include <regex>
#include <map>

typedef std::map<std::string, std::string> PlayerInfo;

void replaceTags(std::string &tagged, const PlayerInfo &player) {
    for(auto const& tag : player) {
        std::regex rx(tag.first);
        tagged = std::regex_replace(tagged, rx, tag.second);
    }
}

int main() {

}



#4920131 C&C on my programming language

Posted by on 07 March 2012 - 11:50 AM

It seems like a good start. Do you have exceptions or are you relying on return codes for the time being? Are your "built in" strings immutable like Java's? It looks like array.add() is var args? Do you have a universal base class?

The nice syntax is great, but like Telastyn it wouldn't be enough to entice me. That said, you have a good base now from which you could start including some more significant language features - the kind of things that could start making productivity differences. Any thoughts on where you want it to go?
  • Closures
  • Templates or Generics
  • Value types
  • References
  • RAII
  • Immutable types
  • ...



#4920102 Replace part of string in C++

Posted by on 07 March 2012 - 10:40 AM

It appears you would input them here, under "compile" and "build", unless you are using a Make file (in which case it would go in the Make file).




PARTNERS