Jump to content

  • Log In with Google      Sign In   
  • Create Account

Banner advertising on our site currently available from just $5!


1. Learn about the promo. 2. Sign up for GDNet+. 3. Set up your advert!


rip-off

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

#5203344 How unreliable is UDP?

Posted by rip-off on 10 January 2015 - 04:00 PM

UDP is about as reliable as the underlying internet protocol. UDP doesn't really introduce any additional unreliability, ignoring factors such as the additional UDP header length causing trouble and a network route configured to punish UDP traffic.

The connection is the connection, that is fact. The point is how the protocol reacts to the connection reliability. TCP uses a suite of algorithms designed to take pressure off the network, while re-sending to ensure the data eventually gets through. UDP does not, it will happily shove packets onto the network at the rate you're sending, and doesn't do anything should those packets get lost, duplicated or re-ordered.

Packet loss is very relevant. As mentioned, you have no control over what circumstances can cause packet loss. You cannot ignore it as a factor. Sure, you could build a probability table of certain types of network behaviour, which might help you pick good defaults.

What is your *real* question? Presumably, if given the kind of figures you are looking for, you will use that to make some kind of decision. What kinds of decisions are you thinking about? TCP vs UDP? Reasonable packet sizes? Reasonable sending rates? Whether to bother implementing an adaptive mechanism that tries to react to packet loss? Something else?


#5202068 GameState, is this a good practice?

Posted by rip-off on 05 January 2015 - 03:17 PM

It would be more typical to model such types in terms of collections of other types, here is an overview of how that might look:
// Class book much like you had it

std::ostream &operator<<(std::ostream &out, const Book &book) {
    return out << '\"' << book.getTitle() << '\"' << ", by " << book.getAuthor();
}

#include "book.h"

class Shelf
{
public:
    Shelf(const std::string &label) : label(label)
    {
    }

    const std::string &getLabel() const;

    const std::vector<Book> &getBooks() const;

    void placeBook(const Book &book);

private:
    std::string label;
    std::vector<Book> books;
};

std::ostream &operator<<(std::ostream &out, const Shelf &shelf) {
    out << "Shelf labelled " << shelf.getLabel() << " contains:\n";
    for (const Book &book : shelf.getBooks()) {
        out << " * " << book << '\n';
    }
    return out;
}

#include "shelf.h"

class Furniture
{
public:
    Furniture(const std::string &description, const std::vector<Shelf> &shelves)
    : 
        description(description),
        shelves(shelves)
    {
    }

    const std::string &getDescription() const;

    const std::vector<Shelf> &getShelves() const;

private:
    std::string description;
    std::vector<Shelf> shelves;
};

std::ostream &operator<<(std::ostream &out, const Furniture &item) {
    out << "Furniture " << item.getDescription() << '\n';
    for (const Shelf &shelf : item.getShelves()) {
        out << " * " << shelf << '\n';
    }
    return out;
}

#include "furniture.h"

class Room
{
public:
    Room(const std::string &name) : name(name)
    {
    }

    const std::string &getName() const;

    const std::vector<Furniture> &getFurniture() const;

    void furnishWith(const Furniture &furniture);

private:
    std::string name;
    std::vector<Furniture> furniture;
};

std::ostream &operator<<(std::ostream &out, const Room &room) {
    out << "Room " << room.getDescription() << '\n';
    for (const Furniture &item : item.getFurniture()) {
        out << shelf << '\n';
    }
    return out;
}

Furniture buildBookShelf() {
    Shelf math("Math");
    for(int i = 0 ; i < 3 ; ++i) {
        math.placeBook(Book("The joy of math, volume " + std::to_string(i + 1), "Matty Mathews"));
    }

    Shelf informatics("Informatics");
    for(int i = 0 ; i < 5; ++i) {
        informatics.placeBook(Book("The joy of information, volume " + std::to_string(i + 1), "Ivy Inverness"));
    }

    std::vector<Shelf> shelves;
    shelves.push_back(math);
    shelves.push_back(informatics);
    return Furniture("Antique oak bookshelf", shelves);
}

Room prepareLibrary() {
    Room library("West facing library");
    Furniture bookshelf = buildBookShelf()
    library.furnishWith(bookshelf);
    return library;
}

int main() {
    Room room = prepareLibrary();
    std::cout << library << "\n";
}

I hope the implementations of the member functions is more or less obvious to you. I've not compiled that, it is possible that there are typos or other errors in the listing.

Couple of points to highlight:

First is that I didn't expose "setters" for all of the members. A common mistake many beginners make is assuming that every field must be exposed for reading and writing. Good OO code is often the opposite, the class exposes enough to be useful, but no more. I took a short cut above, and exposed the collections. Others would encapsulate this detail in various ways. Note how the operations to add to the collection were written in terms of the domain, you place books on a shelf, you furnish a room.

Also, see how we didn't need create different variables for the individual book objects, we create anonymous instances inside the loops and add them directly to the shelf.

Notice how an item of furniture does not have an operation to add a shelf - most pieces of furniture comes as they are and don't change over time. So to create a piece of furniture, you must first describe the shelves, then create the furniture from this description. It would be possible to retain the ability to modify the contents of the shelves, but the example was getting long enough as it is.

You can see already how you pass the objects to and from functions. I've used the C++ stream overloads for this, but that isn't necessary, and might not be a good idea - you might see why by attempting to print the shelves indented once, and the books on the shelf indented twice, while still retaining the ability to print an unindented book directly.


#5202062 do most games do a lot of dynamic memory allocation?

Posted by rip-off on 05 January 2015 - 03:05 PM

I guess the crux of this debate comes down to the definition of "most games". Are we talking exclusively AAA? AAA + professional / full time indies? Hobby games by experienced individuals? Every little game made by everyone, regardless of experience?

The answer to "do most games do a lot of dynamic memory allocation" can probably summed up like so:

Professional teams almost certainly take great pains to avoid dynamic allocation during gameplay, but do not shy away from it during explicit loading times. Generic engines probably must do dynamic allocations to remain flexible for the wide variety of games they are used for, but heavily game specific code (like Norman's) can make certain assumptions to avoid that.

Other than that, it comes down to what is acceptable in your frame budget. Simple games like Asteroids could probably allocate for every game entity created without a problem. I'd argue that it would be over-engineering to bother with techniques like memory pools for a game like that.

I guess a slightly more interesting question might be: "is language support for dynamic memory ownership necessary in a language aimed at professional game programmers".


#5201972 GameState, is this a good practice?

Posted by rip-off on 05 January 2015 - 10:14 AM

Essentially, yes, that is the idea. It depends on the program though. I don't have one of those fancy e-readers, but imagine one that allows for multiple user "profiles" (e.g. shared between a married couple). In such a program, the concept of a "current page" might be independent of the book. So you might have a book, a reader, and the "current page" might be a concept that exists between a particular reader and a particular book.

 

OO is about modelling for *your* program, so the right OO model for your program might look very different from a program that deals with similar objects. Amazon's idea of a book for their online store can very different from that of an e-reader.




#5201549 GameState, is this a good practice?

Posted by rip-off on 03 January 2015 - 10:36 AM

Hi nunobarao. Good attempt.

There is a great heuristic to Object Oriented design called The single responsibility principle. Your current MainGame class has multiple responsibilities: handling the menu, running the game logic, managing levels. It isn't clear to me what the "level" is in this game, the concept appears not to be used.

Some people like resetting variables, others do not. Consider that both std::ifstream and std::string allow you to reset the object's state to being "fresh". However, with such a model comes complexity. I prefer to write classes to be used once, rather than re-used, unless that proves necessary.

Note that you will struggle to come up with an Object Oriented design for such a small program. It is always going to be a little forced and over-engineered.

Note that not everything needs to be in an object. For example, seeding the random number generator should be done once at program startup. You can actually get less randomness to your results by reseeding, though it probably won't affect this game.


#5201540 Yet another global variable debate

Posted by rip-off on 03 January 2015 - 09:43 AM

Your solution with the systems declared in a certain order as private members of an enclosing class is error prone too, anyone can mix the list and you'll spend a lot of time tracking that error. The mixed code looks like working code, so the programmer fixing the new bug wouldn't look at it at first. With that approach you can only comment a lot and expect the comments to be updated if anything changes, and expect everyone to read the comments.

If you don't have any globals, then any dependencies must be passed. Thus, if the order of construction is important, it should be obvious from the need to pass Object A to Object B's constructor.

Yes, a careless programmer might do something silly, but overall this is quite explicit - I would say more so than the order of init / destroy calls in main(), particularly where there is global access it could be easy to add a new dependency (e.g. reference the RenderEngine in a helper function that is ultimately called as part of initialising some other part of the system).

It is a pity that C++ has the surprising behaviour of executing initialiser lists in the order the members were declared, rather than that specified in the initialiser list. While there is a reason for this, it is not obvious, it would be less error prone if C++ mandated that the order of the initialiser list must match the member declaration order to avoid such issues.


#5201408 Yet another global variable debate

Posted by rip-off on 02 January 2015 - 04:57 PM

I've not read the book, but is it possible this is being used to shorten the examples, or is it actually recommended in the main text?


#5201371 Storing binary images and portability

Posted by rip-off on 02 January 2015 - 01:42 PM

Nowadays, the CPU is incredibly fast relative to disk access. Loading compressed data and decompressing it might be faster than loading uncompressed data, depending on how many files you have, how big they are and what kind of compression ratios they get.

Another potential idea is to bundle all the resources into a single (compressed) archive (like a zip file). A single file might is hopefully very contiguous on disk, which should reduce time spent seeking the disk. A virtualised file system can be used, something like Physfs, allowing the rest of the program to be isolated from the details of how the resources are actually located or loaded. Indeed,

Consider benchmarking the different approaches to be sure.

Compiling resources into your executable might slow down your iteration time (unless it is something you can turn on and off quickly), which could be detrimental to the project and should be factored in.


#5201321 Theory behind point and click adventure game programming?

Posted by rip-off on 02 January 2015 - 10:02 AM

Some quick Googling returns plenty of results relating to creating point and click adventure games in Unity, from blog posts to frameworks. What are you searching for, what have you found? Is there anything specific that these kinds of resources are lacking?


#5201319 Random subset of list including specific element(s) in Python

Posted by rip-off on 02 January 2015 - 09:57 AM

There is rarely a "best" way of doing anything. There are just different ways with different trade offs.

To understand any trade-offs you're making, we'd need to understand the broader requirements. How frequently is this called? How important is the random distribution of elements in the new list? Is the "required" element always the same, or is that generated dynamically, and if so, how?

Unless this was in a hot loop being called thousands of times a second, I'd probably do something similar to what has been described.


#5201309 How Should I go About Writting this class to file

Posted by rip-off on 02 January 2015 - 09:36 AM

The simplest way to check for errors is to test the stream object itself, which can be used in a boolean context to determine if any type of error occurred. You only need to test the specific types of failures if you care about this detail:
bool read(std::istream &in, bool &val)
{
    in.read(reinterpret_cast<char *>(&val), sizeof(val));
    return in;
}
You have to be careful as the failure could occur midway through loading an object, so the object's state might be inconsistent. For instance, you might have an invariant where each of the button "names" must have a matching "description". It would be possible to break that invariant if the file got corrupted in the middle of the descriptions array. Note that if this is actually the case, having a single array of structures is probably going to be easier to manage than two parallel arrays.

One way to avoid inconsistent state is to read the state into a temporary object, and swap it with the real object on success:
bool read(std::istream &in, ButtonData &buttonData) {
    ButtonData temp;

    read(in, temp.ButtonName);
    read(in, temp.ButtonDescription);
    read(in, temp.FlashTime);
    read(in, temp.OffFlashTime);

    int32_t type = 0;
    read(in, type);
    temp.Type = type;

    int32_t othermembers = 0;
    read(in, othermembers);
    temp.othermembers = othermembers;

    if (in) {
        std::swap(buttonData, temp);
        return true;
    }
    return false;
}
You'll not that the object passed in is only modified once all data has been loaded successfully. If you implement a custom swap, you can even avoid the overhead of copying the vectors (swapping vectors directly is very efficient).

Another approach to error handling is to use exceptions.


#5200999 Executable Jar File not Working...

Posted by rip-off on 31 December 2014 - 09:49 AM

That message appears to indicate that no file called "hang.jar" exists. Are you viewing with file extensions enabled? Is the file just called "hang", or does it have an extension?

You can use the "dir" command to see the contents of the directory you are in.


#5200995 Can you hack together the network coding of an MMO?

Posted by rip-off on 31 December 2014 - 09:33 AM

It depends on how seriously you take the "Massive" part of MMO. Typically, it means that there can be lots of concurrent players. I don't think you'll arrive at an implementation that can really handle the kind of numbers that MMOs aspire to by "hacking", you have to have a design.

However, if by "MMO" you mean a large open world RPG game, without necessarily needing tens of thousands of concurrent players, then yes you can probably hack something together that could support on the order of hundreds of players, depending on how complex the rest of the game is, etc.

Most of the challenges come from when you intend to exceed the capabilities of a single server to run the entire game, it gets a lot more complex at that point.


#5200993 How Should I go About Writting this class to file

Posted by rip-off on 31 December 2014 - 09:27 AM

One of the simplest ways to serialise variable length data structures is to write the length first, then write all the elements one after the other.

Something like this:
void write(std::ostream &out, int32_t number);
void write(std::ostream &out, float number);

void write(std::ostream &out, const std::string &string) {
    int32_t size = string.size();
    write(out, size);
    out.write(string.c_str(), size);
}

template<typename T>
void write(std::ostream &out, const std::vector<T> &items) {
    int32_t size = items.size();
    write(out, size);
    for (int32_t i = 0 ; i < size ; ++i) {
        write(out, items[i]);
    }
}

void write(std::ostream &out, const ButtonData &buttonData) {
    write(out, buttonData.ButtonName);
    write(out, buttonData.ButtonDescription);
    write(out, buttonData.FlashTime);
    write(out, buttonData.OffFlashTime);

    // TODO: Casting might be required here, I haven't compiled this
    // You might want to add another function to handle uint32_t too (or other sized values)
    // Could be easier & clearer if you use the explicitly sized variables in the structure itself
    int32_t type = buttonData.Type;
    write(out, type);
    int32_t othermembers = buttonData.othermembers
    write(out, othermembers);
}
For reading, it is very similar:
void read(std::istream &in, int32_t &number);
void read(std::istream &in, float &number);

void read(std::istream &in, std::string &string) {
    int32_t size = 0;
    read(in, size);
    string.resize(size);
    in.read(&string.front(), size);
}

template<typename T>
void read(std::istream &in, std::vector<T> &items) {
    int32_t size = 0;
    read(in, size);
    items.resize(size);
    for (int32_t i = 0 ; i < size ; ++i) {
        read(in, items[i]);
    }
}

void read(std::istream &in, ButtonData &buttonData) {
    read(in, buttonData.ButtonName);
    read(in, buttonData.ButtonDescription);
    read(in, buttonData.FlashTime);
    read(in, buttonData.OffFlashTime);

    int32_t type = 0;
    read(in, type);
    buttonData.Type = type;

    int32_t othermembers = 0;
    read(int, othermembers);
    buttonData.othermembers = othermembers;
}
Error checking has been omitted from this example for brevity.


#5200895 do most games do a lot of dynamic memory allocation?

Posted by rip-off on 30 December 2014 - 05:39 PM

However there are three separate per-MB costs for large allocations. As the chart shows these add up to approximately 400 μs per MB. So, allocating an 8 MB buffer every frame (perhaps to hold a 1080p RGBA image) can easily waste 3.2 ms (3,200 μs) of CPU time per frame.


shocking, isn't it ? heap allocations and de-allocations can be SLOW compared to the data segment. that's why i use the data segment when i can, and the heap only when i have to. i mean, why run slower (with a potential for heap errors) when you can run faster (with no heap errors guaranteed)? sure a one time malloc on the heap of a target list isn't much different than a statically declared target list. but there really isn't a limit on the data segment anymore is there? so why not use it? its faster than heap (no malloc/ free overhead) and less error prone (no heap errors possible). dynamic memory is slower that static (doe to allocation overhead), only use dynamic when you truly need dynamic structures, such as a unity type game engine that can load assets until it runs out of ram.

The data segment isn't free. If there is interesting data, it must be loaded from disk, which is very likely to be a more expensive than zeroing a free page and running the various allocation data structures / algorithms. If the data is zero, something must still zero it, so you just have the allocation cost. In the grand scheme of things the flexibility that you get for the cost of a couple of allocations seems like a worthy trade-off to consider.

Then there are the other costs, using the data segment means essentially using global state, which is affects code readability, maintenance and the ability to reason about multi-threaded behaviour.

I'm not sure it is quite the win you're making it out to be.




PARTNERS