Jump to content

  • Log In with Google      Sign In   
  • Create Account

SyncViews

Member Since 06 Feb 2011
Offline Last Active Sep 19 2015 01:00 PM

Posts I've Made

In Topic: C++ Loading config files, with extend/inheritance and full error handling

06 August 2015 - 04:46 AM


I think he meant that a required field can be defined in any of the extending files, so he can't use individual schemas for all of them, as almost every field is effectively optional when you look at separate files.

Yes, so while in that project we do have schema's and one of the Java things to validate and load an XML into a generated class, nearly every element and attribute is "optional" in the schema, and there is no effective validation of file inheritance/extension related things.

 

 

 


ake a look at something like CodeSynthesis, LMX, or XmlPlus. Given an XSD file, they generate the matching C++ object model, complete with serialization code. You no longer have to worry about all that mundane boilerplate, and it's not too difficult to integrate into a build process.

Wasn't aware of XmlPlus. I suppose may be able to get LGPL to work, if it is practical to make it into a self contained DLL without any inclusion of the main code. I think a commercial licence would be a hard sell in most of the cases I have run into this issue though, since only a small number of custom data files/schemas. Also doesn't look like they solve the inheritance/extension issue.

 

 

 


Just been looking in to some file formats my self and SQLite is looking good. And Tutorials.

Is there any compelling reason to use SQL as the primary data definition format? I have used SQL in many context's, but only as something for the software to manipulate. It seems a big ask for people (often non-programmers) to define content directly in SQL, especially since you end up with a fairly nasty foreign key structure for when things contain any kind of list/collection.


In Topic: default install folder for windows game

02 August 2015 - 04:02 AM

And also remember that especially outside games, many users are not able to elevate. Software will be installed by IT admins (possibly in some bulk capacity, I am not up to speed with the Window's domain admin stuff) and after that it needs to work correctly in a multi-user environment. They really dislike having to do special installs for software that does not quiet work like that, and only developers can get local admin.

 

I suspect there are other requirements they have, like not being able to put pwned.exe/dll in some directory using a visitor/guest/contractor login and have your piece of software execute it next time someone tries to use that conference room.


In Topic: XP, Level, Health,Damage progression of player, enemy and weapons

01 August 2015 - 04:30 AM

So what aspect exactly?

 

Normally when there is many levels,  I see stats implemented via some sort of formulas, which you can play with to get the results you want. Some stats may be linear, others exponential.

 

e.g.:

 

stealth_detect_range = 10 + level * 5

hp = 100 + level*level

resist = 0.9 - 0.5/level

 

You can apply such things to both players and NPC's, and to randomly generated weapons/items. NPC level is selected based on player level, possibly with some random variation, and possibly including difficulty setting.

 

When there is fairly few levels (e.g. specific skills you spend skill points on, rather than or in addition to a "level" that buffs everything), I tend to see games just explicitly pick values for each level. Sometimes a skill level will have an entirely new effect compared to the one before it.

 

 

It mostly just a question of deciding what you want level's/skills to do, then playing it a lot to try and make it balanced/fair.


In Topic: Pass anything as constructor without varargs

01 August 2015 - 02:44 AM


@SyncViews Well, I don't know why, but I really like keeping the interfaces of some stuff simple, and don't really like the idea about having to instatiate a pool for each type (Since I also probably won't control all types in the end). Those memory-pools look very interesting, though!

 

Well, you would still have a simple interface with a memory pool approach. I can't remember the exact steps off hand, but it should be possible to make new/delete directly use your custom allocator. If you do that you even get rid of this "recycleEvent" thing and can just let the default delete keyword, constructors and destructors take care of it. In some places you might even find you can just put the event on the stack if it is not queued and handled asynchronously, so you can do "MyEvent evt(...); handler->handleNow(&evt);"

 

EDIT:

From a quick looked based on memory and some searches, you should be able to do something like this if you want a global allocator. If you don't want a global allocator, I think you can do some clever things with how you have the new/delete, or you could just make the alloc/free part of that EventHandler class, and use a small header template with argument forwarding and placement new/delete to construct and destruct the actual objects. You either would not be able to do "delete p; delete[] arr", or would need to insert a pointer back to the EventHandler in the memory layout for operator new to use. 

 

  • I am hoping this code is mostly correct, but it also mostly from memory smile.png
  • You can still put events directly on the stack, as globals, inside other classes etc. They will be allocated/deallocated according to the normal rules in that case.
  • I found on MSVC 2015 that for new[], delete[], that while new[] was given the total size of memory, that delete[] seemed to get given what looked like sizeof(T). Not very helpful... As a result I had that bit of logic to track the actual allocation size.
  • If you are using multiple threads, the alloc/free methods need to be thread safe. This may be possible using the available atomic operations, I have no thought about it.
  • The alloc function may choose to allocate lots of internal memory at once. e.g. instead of allocating say 32byte objects one at a time to pool, it might opt to get take a 4KB page from the OS and use that, instead of making lots of small long-lived allocations that may fragment the C++ heap.
  • The alloc function may also decide to group different sizes into "buckets". Eg. everything from 65 to 128 bytes might just use a single 128 byte pool. Objects/Events of the size naturally use the same pool regardless.
  • It should be possible to avoid the use of std::map/std::unordered_map. If you implement with a small number of buckets, a simple integer search is very fast. If you choose the bucket size carefully, you might be able to "jump" directly to the right one using simple arthritic.
/**@brief Minimal base for all event types.*/
class Event
{
public:
    static void* operator new(size_t size)
    {
        return genericPoolAllocator.alloc(size);
    }
    static void operator delete(void *p, size_t size)
    {
        genericPoolAllocator.free(p, size);
    }
    static void* operator new[](size_t size)
    {
        auto realSize = size + sizeof(size_t);
        auto tmp = (char*)genericPoolAllocator.alloc(realSize);
        *(size_t*)tmp = realSize;
        return tmp + sizeof(size_t);
    }
    static void operator delete[](void *p, size_t size)
    {
        auto realP = ((char*)p) - sizeof(size_t);
        auto realSize = *(size_t*)realP;
        genericPoolAllocator.free(realP, realSize);
    }

    Event() {}
    virtual ~Event() {}
private:
};
class KeyEvent : public Event { ... };

class Nested : public Event { Event a, b; };
struct Container { Event a, b; };

int main()
{
    {   std::cout << "Event" << std::endl;
        Event *evt = new Event();
        delete evt;
    } {   std::cout << "KeyEvent" << std::endl;
        KeyEvent *evt2 = new KeyEvent();
        delete evt2;
    } {   std::cout << "Event array" << std::endl;
        Event *evt = new Event[100];
        delete[] evt;
    } {   std::cout << "Nested" << std::endl;
        Nested *evt2 = new Nested(); //single custom allocation for outter object
        delete evt2;
    } {   std::cout << "Container" << std::endl;
        Container *evt2 = new Container(); //doesnt use custom allocator, because container does not
        delete evt2;
    } {   std::cout << "KeyEvent stack/auto" << std::endl;
        KeyEvent stack; //just lives on the stack
    }
}

In Topic: Pass anything as constructor without varargs

31 July 2015 - 08:51 AM


The only problem is that I don't want to create a pool for each of them

 

 


std::map> eventPool;

,>

 

But isn't that exactly what you have done? Is it really such a big step to make a "SimplePool<T>" then explicitly instantiate it for each type in some collective factories? Then just do say "myEventFactory->createKeyEvent(KeyEvent::KEY_DOWN, keyCode, modifierKeys)".

 

 

Otherwise for a more complex solution Id just look at getting a pool memory allocator. Since this only actually deals with raw memory of certain sizes, the type does not matter, and you avoid that expensive map lookup. Instead at that level your just getting or freeing a block of initialised memory (potentially with optimisations, like allocating a full page worth of blocks at once). You can have a pool for different sizes, e.g. up to 16 bytes, 32bytes, 64, 128, 256, etc.

 

The user of the system then deals with constructing and destructing the right object into it (e.g. new/delete). If you have many event types with similar sizes, this also reduces the required size of the pool. And there is no special requirements on your pooled objects (at least at this level, e.g. your other event code might mandate a base class), e.g. they don't even need to be moveable or have any particular constructor signature.


PARTNERS