Welcome, Guest. Please login or register. Did you miss your activation email?

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - Mutoh

Pages: [1] 2 3
1
General / Re: Client-host networking strategy
« on: March 29, 2015, 10:44:21 pm »
Ah, ZeroMQ provides a custom protocol, right? I think I'm fine with what SFML already provides in regards to network, I only need help on how to manage my data. ;)

I noticed that the client packet receiving strategy from my last post wouldn't work with input combinations that required nicely timed combos. I cannot ignore any packets, but I have to ensure that they are processed in the order they were sent. I think for that I will timestamp the packets.

2
General / Re: Client-host networking strategy
« on: March 27, 2015, 03:09:16 pm »
That was an insightful read. I think I should store whole previous sequences of graphical states in a buffer, then. If I'm on sequence 20 and I receive a client packet in which the client acknowledges the sequence 15, I will compare sequence 15 and sequence 20 in my buffer and send the delta to this client.

Of course, the buffer should have a certain limit size, probably determined dynamically. If the client acknowledges a sequence that is no longer in the buffer I can send them the whole current graphical state - instead of sending it whole every X period of time. If the client's acked sequence is, say, 7 sequences behind the latest server sequence and my buffer is storing only the last 5 states, I can probably expand the buffer's limit to 7 for some time.

But I still don't have certainty on how the server would process input. Should I have the client keep sending packets to the server until it receives an ack response from the server? Let's say that, for example, a client whose last acked sequence was 15 will keep sending packets with their current input to the server. The client ends sending three packets before receiving the server's ack, all with different input data. The first input packet to arrive to the server was the second one sent. Upon acknowledge, should the server ignore all further packets it receives from the client that were marked with sequence 15? That is, should it ignore the first and third packets sent by the client while the client waited for a response?

3
General / Client-host networking strategy
« on: March 23, 2015, 05:03:18 pm »
In the process of making a platformer with plans for a local multiplayer I have begun considering giving it an online multiplayer as well. I have done some research on networking an I want some input on my strategy.

Overall idea:
(click to show/hide)

---

Clients would only send input to the host, perhaps with intervals of once every [physics' step duration] seconds, but how the host should organize its received input packets is a more blurry area for me.

Should I use TCP when clients are sending inputs to the host, so that no inputs are received out of order by the host? I plan on having the host not wait for inputs from its clients, and keep gathering the inputs it receives into a queue. The queue would be depleted as it advances its physics' steps. The following pseudo-code should exemplify the procedure:
(click to show/hide)

I am worried about possible too bothersome delays between the clients and response time. After all, it takes on average 100-200ms for a client to receive response from the packet it just sent. I am willing to limit the online gameplay only to LAN connections if the only solutions are complicated predictions - a client-host architecture is still effective against preventing the lockstep asynching problem of peer-to-peer and even allows late-join.

---

I apologize for writing so much, but I want some critiques, suggestions and overall guiding from developers here with more experience with networking in games and SFML, as it hasn't been long since I started studying networks and I have done pretty much no experiments. Developing of the game has only started and this is the best time to decide on such critical points.

4
SFML projects / Re: SfmlExt
« on: January 06, 2015, 04:35:25 pm »
Aw, no namespace? That is always nice having in libraries you want to distribute.

Also, in your second example code:

    // load that archive
    TexArchiveReader reader;
    assert(reader.open("out.bin"));

Even if is just example code, I heard that it's not wise to put fundamental actions inside assert's, because in some compilation settings the entire assert line might be optimized away - and with it, reader would never open "out.bin".

5
SFML projects / Re: Currently Unamed 2d Tile Map RPG Game
« on: December 26, 2014, 06:52:24 am »
Well, this is programmer art (from other games at that) but that's ok, it only does not really count as a feature of your game. What can the player do? Do these doors work? Can Link attack?

6
SFML projects / Re: Dwell - A Retro Sandbox Survival MMO
« on: December 23, 2014, 05:04:09 am »
This game is gorgeous, congrats! I had thought about making a "top-down Minecraft" game some day or another after being, well, dissatisfied with Dwarf Fortress, but I don't think it would be on par with the stylishness of this.

7
SFML projects / Re: Currently Unamed 2d Tile Map RPG Game
« on: December 23, 2014, 04:32:27 am »
This goes along better, some ideas don't need to be scraped, just reworked (which often means making it simpler).

8
SFML projects / Re: Currently Unamed 2d Tile Map RPG Game
« on: December 23, 2014, 04:20:29 am »
We'll wait for the pics (and videos or demos, why not?) before saying whether you should give up a year of work or not. The value of something is not directly dictated by the amount of labor put into it. :)

9
SFML projects / Re: Witch Blast (dungeon crawl shooter)
« on: December 23, 2014, 02:55:46 am »
The project is looking pretty good! I don't remember playing so seriously an alpha indie game. I hope that it already has an ending, or it will feel like all my hard work to achieve this goal was put to waste. ;D

I just beat the Wererat King but died at the dungeon following it. Fairies are beasts, and I'll admit that I severely underestimated the rat king from the first time I saw him in the early videos as I thought that he was a low-tier boss.

10
SFML projects / Re: Image Atlas Generator
« on: December 21, 2014, 05:54:38 pm »
This is great! I was almost writing something like this myself, but you saved me a lot of time. Followed as well, because I'll be needing it in the future. :D

11
Graphics / Re: Platform game / tile permeability / Box2D
« on: June 27, 2014, 03:00:46 pm »
Absolutely right, slopes are hell. ;D

12
Graphics / Re: Platform game / tile permeability / Box2D
« on: June 27, 2014, 06:42:38 am »
Interesting, I've been dealing with this "permeability" concept just recently in my own game (with some personal success). :) Here's what I've been doing:

My tiles are all represented by a "Tile" base class that has "dislodgeX" and "dislodgeY" methods, a hitbox and any flags I want within it.

Every time I move an entity, I do an equivalent to the following:

void Entity::move (const sf::Vector2f& delta) {
    moveHitbox(delta.x, 0);
   
    for (Tile& tile: getStage().tiles)
        tile.dislodgeX(*this, delta.x);
       
        moveHitbox(0, delta.y);
   
    for (Tile& tile: getStage().tiles)
        tile.dislodgeY(*this, delta.y);
}

And a Tile's ::dislodgeX method, for example, looks somewhat like the following:

void Tile::dislodgeX (Entity& ent, float delta) const {
        if (!ent.getHitbox().intersects(this->getHitbox())) return;
        // ---
       
        if (delta < 0) {
                // Entity is moving left. [T]<-[E]
               
                ent.setHitboxLeft(this->getHitbox().left + this->getHitbox().width);
                // A similar method to the above makes it so that the entity and the tile's
                // hitboxes are now perfectly adjacent. [T|E]
        }
        else if (delta != 0) {
                // Entity is moving right [E]->[T]
               
                ent.setHitboxLeft(this->getHitbox().left - ent.getHitbox().width);
                // [E|T]
        }
}

How could we implement permeability? What I did is somewhat complex or very simple, it depends on how you look at it. My "Entities" have two std::set<const Tile*>'s within them that keeps track of the impermeable tiles in which they are inside (one set is for the previous tick, and another is for the new tick), and here is how make the Tiles use these sets:

struct Entity {
        std::set<const Tile*> tilesInside, prevTilesInside;
       
        /* ... */
       
        void move (const sf::Vector2f&);
        bool wasInside (const Tile&) const;
};

void Entity::move (const sf::Vector2f& delta) {
    prevTilesInside = tilesInside;
        tilesInside.clear();
        /* Alternatively: prevTilesInside = std::move(tilesInside); */
       
        // ---
       
        moveHitbox(delta.x, 0);
   
    for (Tile& tile: getStage().tiles)
        tile.dislodgeX(*this, delta.x);
       
        moveHitbox(0, delta.y);
   
    for (Tile& tile: getStage().tiles)
        tile.dislodgeY(*this, delta.y);
}

bool Entity::wasInside (const Tile& tile) const {
        return prevTilesInside.find(&tile) != prevTilesInside.end()
                || tilesInside.find(&tile) != tilesInside.end();
}

/*
        ...
*/


void Tile::dislodgeX (Entity& ent, float delta) const {
        if (!ent.getHitbox().intersects(this->getHitbox())) return;
        // ---
       
        if (delta < 0) {
                if (!this->allowsEnteringLeft && !ent.wasInside(*this)) {
                        ent.setHitboxLeft(this->getHitbox().left + this->getHitbox().width);
                }
                else ent.tilesInside.insert(this);
        }
        else if (delta != 0) {
                if (!this->allowsEnteringRight && !ent.wasInside(*this)) {
                        ent.setHitboxLeft(this->getHitbox().left - ent.getHitbox().width);
                }
                else ent.tilesInside.insert(this);
        }
}

I tried many methods before this one, each more convoluted than the other, but this was what worked best. It's effective. And why sets instead of a single pointer, you may ask? The entity can always be inside more than one tile at once. ;)

Good luck with your project! I wouldn't recommend using physics engines, indeed, at least not for a platformer that requires tight controls - or, mainly, for learning. As long as you keep your game timestep-fixed (constant framerates) and you stick to using AABB rectangles for collision, you won't need them. But it's not like you won't have some work to do, mainly when dealing with accelerations and gravity. It's either "stick to a engine and cut off work" or "write it up yourself and have complete control over the physics". :)

13
Actually, I won't need to have my friend test this. Knowing that this is a RenderTexture problem actually solves all problems! :D The exact cases that worked were Sprites that were drawn directly into the screen and had Textures that were loaded directly from a file, and the ones that didn't were because I was drawing Sprites whose Textures were actually from RenderTextures that pre-rendered elements of my level (tiles). Thanks very much, expl0it3r!

I'm sorry for not providing a minimal enough code, but I was afraid that if I "minimized the problem too much" I would accidentally remove the actual problem. For example, I almost considered rendering the tile directly into the RenderWindow in the sample to minimize the code. If I did, we wouldn't have found out that it was a problem with RenderTexture.

And in fact, I did provide my friend versions with the console visible, mainly because then we would know through SFML itself what was happening (it prints into the console whether the file couldn't be opened, if the image is corrupted or if it's empty, etc.) But, surprisingly enough, this OpenGL message never showed up for any of us. It indeed would have saved plenty of time.

Oh, I don't know why I decided to scatter terminates through the sample. :) But, as I said, they are actually substitutes for exceptions that I would throw. Well, I just thought that the sample program wouldn't have any reason to continue if it came across any of those errors.

Again, thanks for the help. I'm sorry if I wasn't helpful enough at any time. :D

EDIT: Even then, I don't think I'll tweak enough my game so that it works on Windows XP, if it's a case as "obscure" as this. It's good to know that it's beyond my reach, nevertheless, so I don't really have to worry about it. |)

14
Nope, no copying. :) I had tested beforehand deleting the copy operators, just as you recommend, wintertime, but no compiler errors.

Here, now we have a code that anyone can test!

#include <vector>
#include <cmath>
#include <stdexcept>
#include <iostream>

#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/RenderTarget.hpp>

// ---

class Tileset {
        public:
                Tileset (unsigned int tileWidth, unsigned int tileHeight)
                        : m_tileSize(tileWidth, tileHeight)
                {
                        m_drawer.setTexture(m_texture);
                }
               
                Tileset (const Tileset&) =delete;
                Tileset& operator=(const Tileset&) =delete;
               
                ~Tileset() {
                        std::cout << "I will die horribly.\n\n";
                        std::terminate();
                }
               
                bool loadFromFile (const std::string& path) {
                        if (m_texture.loadFromFile(path)) {
                                prepareTiles();
                                return true;
                        }
                       
                        return false;
                }
               
                bool loadFromMemory (const void* data, size_t size) {
                        if (m_texture.loadFromMemory(data, size)) {
                                prepareTiles();
                                return true;
                        }
                       
                        return false;
                }

                void drawTile (int index, const sf::Vector2u& posId, sf::RenderTarget& tar) const {
                        auto where = m_tileSize;
                        where.x *= posId.x;
                        where.y *= posId.y;
                       
                        m_drawer.setTextureRect(getTile(index));
                        m_drawer.setPosition(where.x, where.y);

                        tar.draw(m_drawer);
                }

                size_t getTileCount() const {
                        return m_tiles.size();
                }
               
                const sf::IntRect& getTile (int index) const {
                        return m_tiles.at(index);
                }

        private:
                void prepareTiles() {
                        m_texture.setSmooth(true);
                       
                        sf::Vector2u textureSize = m_texture.getSize();

                        sf::Vector2u nTiles = sf::Vector2u (
                                        std::ceil(static_cast<float>(textureSize.x) / m_tileSize.x),
                                        std::ceil(static_cast<float>(textureSize.y) / m_tileSize.y)
                                );

                        m_tiles.clear();
                       
                        for (int row = 0; row < nTiles.y; row++) {
                                for (int col = 0; col < nTiles.x; col++) {
                                        m_tiles.emplace_back (
                                                        col * m_tileSize.x,
                                                        row * m_tileSize.y,
                                                        m_tileSize.x,
                                                        m_tileSize.y
                                                );
                                }
                        }
                }
               
                sf::Vector2u m_tileSize;
               
                mutable sf::Sprite m_drawer;
                sf::Texture m_texture;
                std::vector<sf::IntRect> m_tiles;
};

// ---

#include <map>
#include <string>
#include <memory>
#include <fstream>
#include <iterator>

// ---

class LevelData {
        public:
                static std::shared_ptr<const Tileset> getTileset (const std::string& tilesetName, const sf::Vector2u& tileSize) {
                        auto found = tilesets.find(tilesetName);
                       
                        if (found != tilesets.end()) {
                                return found->second;
                        }
                        else {
                                auto tileset = std::make_shared<Tileset>(tileSize.x, tileSize.y);
                               
                                #define TAKE_IT_EASY
                                // #define CPP_FILES
                               
                                #ifdef TAKE_IT_EASY
                                if (!tileset->loadFromFile(tilesetName)) {
                                        std::cerr << "Tileset couldn't load!\n\n";
                                        std::terminate();
                                }
                                #elif defined(CPP_FILES)
                                std::ifstream stream(tilesetName, std::ios::binary);
                                       
                                if (!stream) {
                                        std::cerr << "Couldn't open the file.\n\n";
                                        std::terminate();
                                }
                               
                                std::vector<char> buffer(
                                                (std::istream_iterator<char>(stream)),
                                                (std::istream_iterator<char>())
                                        );
                               
                                if (!tileset->loadFromMemory(buffer.data(), buffer.size())) {
                                        std::cerr << "Tileset couldn't load!\n\n";
                                        std::terminate();
                                }
                                #else
                                std::FILE *file;
                               
                                if (!(file = std::fopen(tilesetName.c_str(), "rb"))) {
                                        std::cerr << "Couldn't open the file.\n\n";
                                        std::fclose(file);
                                        std::terminate();
                                }
                               
                                auto beg = std::ftell(file);
                                std::fseek(file, 0, SEEK_END);
                                auto size = std::ftell(file);
                                std::fseek(file, beg, SEEK_SET);
                               
                                char buffer[size];
                                std::fread(buffer, 1, size, file);
                               
                                if (!tileset->loadFromMemory(buffer, size)) {
                                        std::cerr << "Tileset couldn't load!\n\n";
                                        std::fclose(file);
                                        std::terminate();
                                }
                               
                                std::fclose(file);
                                #endif
                               
                                tilesets.insert({ tilesetName, tileset });
                                return tileset;
                        }
                }
       
        private:
                typedef std::map<std::string, std::shared_ptr<Tileset>> TilesetMap;
                static TilesetMap tilesets;
};

LevelData::TilesetMap LevelData::tilesets;

// ---

#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/RenderTexture.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Window/Event.hpp>

int main() {
        sf::RenderWindow window(sf::VideoMode(640, 480), "test");
       
        sf::RenderTexture tex;
        tex.create(640, 480);
       
        sf::Sprite sprite;
        sprite.setTexture(tex.getTexture());
       
        if (auto tileset = LevelData::getTileset("tile.png", sf::Vector2u(45, 45))) {
                for (sf::Vector2u pos(0, 0); pos.x < tileset->getTileCount(); ++pos.x) {
                        tileset->drawTile(0, pos, tex);
                }
        }
       
        tex.display();
       
        while (window.isOpen()) {
                sf::Event event;
               
                while (window.pollEvent(event)) {
                        switch (event.type) {
                                case sf::Event::Closed: return 0;
                        }
                }
               
                window.clear();
                window.draw(sprite);
                window.display();
        }
}

At LevelData::getTileset, you can toggle those defines at will. In the actual code of my project I am using the "C files" configuration because I just can't slurp data out of C++ files without getting it corrupted - mainly, it eats away any blank characters. If the file has "a b c" as its contents, only "abc" will be extracted. This bug manifested itself even when I executed this stand-alone code in a new "SFML configured" CodeBlocks project. And yes, I have attested that the TAKE_IT_EASY version has problems in my friends' Windows XP computers.

Also, in the actual code, "terminates" are exceptions that I throw, and Tileset's destructor only terminates the program as proof that no Tilesets are destroyed in inappropriate times. You can also remove that once the point comes across and it has become annoying.

getTileset is actually called after a lot of XML parsing by LevelData, but it's nothing that should actually interfere with the functionalities of getTileset and the Tilesets themselves.

Attached is the tile.png I used.

15
Well, I wouldn't doubt that this code would work.

[...], but there are times in which the Textures work as expected in their computers.

I actually verified that, if I don't wrap the resource loading with my own methods, they are loaded without a problem. It happened accidentally because, in one of the versions I sent to my friend, all textures but one were loaded through my wrapper loading method - the exception texture was the one that worked. Now, you could say that I was being careless with the loading in my wrapper methods, but they are actually as simple as:

#pragma once

// <abx/Tileset.h>
// ---

#include <vector>
#include <cmath>
#include <stdexcept>

#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/RenderTarget.hpp>

// ---

namespace abx {
       
        class Tileset {
                public:
                        Tileset (float tileWidth, float tileHeight)
                                : m_tileSize(tileWidth, tileHeight)
                        {
                                m_drawer.setTexture(m_texture);
                        }
                       
                        // Here, it doesn't work. But if I use a stand-alone Texture, it will.
                        bool loadFromFile (const std::string& path) {
                                if (m_texture.loadFromFile(path)) {
                                        // It enters the clause and is able to prepare the tile rectangles.
                                        prepareTiles();
                                        return true;
                                }
                               
                                return false;
                        }

                        void drawTile (int index, const sf::Vector2f& posId, sf::RenderTarget&) const {
                                auto where = m_tileSize;
                                where.x *= posId.x;
                                where.y *= posId.y;
                               
                                m_drawer.setTextureRect(getTile(index));
                                m_drawer.setPosition(where);

                                tar.draw(m_drawer);
                        }

                        size_t getTileCount() const {
                                return m_tiles.size();
                        }
                       
                        const sf::IntRect& getTile (int index) const {
                                return m_tiles.at(index);
                        }

                private:
                        void prepareTiles() {
                                m_texture.setSmooth(true);
                               
                                // If the texture really wasn't loaded, then the return value of this would
                                // pretty much be equal to sf::Vector2u(0, 0), right?
                                sf::Vector2u textureSize = m_texture.getSize();

                                sf::Vector2u nTiles = sf::Vector2u (
                                                std::ceil(static_cast<float>(textureSize.x) / m_tileSize.x),
                                                std::ceil(static_cast<float>(textureSize.y) / m_tileSize.y)
                                        );

                                m_tiles.clear();
                               
                                // This means that there wouldn't be enough tiles "prepared" for ::drawTile.
                                for (int row = 0; row < nTiles.y; row++) {
                                        for (int col = 0; col < nTiles.x; col++) {
                                                m_tiles.emplace_back (
                                                                col * m_tileSize.x,
                                                                row * m_tileSize.y,
                                                                m_tileSize.x,
                                                                m_tileSize.y
                                                        );
                                        }
                                }
                                // An index beyond the limits of the rectangle vector would be provided by my class
                                // that parses Tiled levels, and std::vector::at would throw an exception at
                                // ::drawTile, but this doesn't happen. The size of the texture is loaded, but the pixels
                                // aren't.
                        }
                       
                        sf::Vector2f m_tileSize;
                       
                        mutable sf::Sprite m_drawer;
                        sf::Texture m_texture;
                        std::vector<sf::IntRect> m_tiles;
        };
       
}

(The class is actually spread between .h and .cpp)

It is also not a memory management issue, as my Tileset's remain alive until the application finishes execution. No copying even is done. It also happens within my ResourceManager classes, which operate almost the same way, but with templates for storing the resources.

I will try to provide, later, a program that isolates better the problem. In sum, it's almost as if the started being bratty only when deep enough in the call stack. I could be doing something wrong, but, c'mon, how much potential for bugs does forwarding a string actually have?

Pages: [1] 2 3