@1, What I think we need is something like your game-server-base project -- a core that provides the base implementations for what you need most i.e. audio, graphics, networking, ...) but which doesn't do any hand-holding like BGT does (i.e. providing you every single function you could ever need). I think C++ might be better for something like this; I'd be happy to release to you an engine I was going to use in-house for a game I wanted to develop but said development got stalled because of college and other things getting in the way. It provides you menus, audio, TTS and a very basic graphics/event handling API (as well as, of course, input handling through keyboard/joystick), but it definitely doesn't hold your hand at all. I know, people will complain about how C++ is so hard to use and whatnot, but as I and many others have said, C++ isn't a language that's overly difficult to learn once you get rid of the fobia that you have to manage all the memory allocations and all that. In fact, here's one of my map serialization functions that you could use to serialize a map into JSON and XML either to a file or to memory:
// First, define the map struct (for an RTs-like game)
// Includes (required)
#include <iostream>
#include <string>
#include <fstream>
#include <cereal/types/vector.hpp>
#include <cereal/types/unordered_map.hpp>
#include <cereal/types/utility.hpp>
#include <cereal/types/array.hpp>
#include <cereal/archives/json.hpp>
#include <cereal/archives/xml.hpp>
#include <cereal/archives/binary.hpp>
#include <cereal/archives/portable_binary.hpp>
#include <cereal/access.hpp>
// Define the struct now...
struct Map {
public:
unsigned long long Rows, Columns, Plains, Overpasses;
std::vector<std::string> NorthSouthPaths, EastWestPaths;
std::unordered_map<std::string, std::vector<std::string>> Scripts;
std::string Name, Objective;
std::array<unsigned long long, 4> Resources;
private:
friend class cereal::access;
template<class Archive>
void serialize(Archive &ar) {
ar(Name, Objective, Rows, Columns, Plains, Overpasses, NorthSouthPaths, EastWestPaths, Scripts, Resources);
}
};
// And now, let's look at the map serialization function that translates your map into XML:
bool SerializeMapToXML (std::string file, struct Map map) {
try {
std::ofstream stream (file, std::ios::trunc|std::ios::out);
cereal::XMLOutputArchive archive(stream);
archive(map);
return true;
} catch (...) {
return false;
}
}
While this may or may not demonstrate good or bad coding practices, it does demonstrate how you might go about serializing a map:
Define a standard C++ struct like you would any other struct.
In the struct, befriend cereal::access. This makes it possible to keep the archive() method private so that it cannot be called by anything or anyone else but Cereal.
In your serialize() function, call your archive parameter (in my case 'ar') with all the items you want to serialize.
Then, serialize it to a file like my function does. (The catch(...) exception handler indicates that we don't really care about the error; if one occurs, then just return false. I could've handled this better, but when I did write it I was a bit frustrated since I couldn't figure out a good map loading/saving library other than serial, so was just trying to throw together an implementation.)
That handles serialization to a file, but what about memory? Same concept with struct definition (you only need to define it once), different function:
std::stringstream SerializeMapToXMLInMemory (struct Map map) {
try {
std::stringstream ss;
cereal::XMLOutputArchive archive(ss);
archive(map);
return ss;
} catch (...) {
std::stringstream ss("error");
return ss;
}
}
And, what about deserialization both from a file and from memory? Again, same concept, different function:
From a file:
template<typename T> std::pair<T, bool> DeserializeMapFromXML (std::string file) {
try {
std::ifstream stream (file);
cereal::XMLInputArchive archive(stream);
Map map;
archive(map);
return std::make_pair(map, true);
} catch (std::exception &ex) {
return std::make_pair(ex.what(), false);
} catch (...) {
return std::make_pair("Unknown error", false);
}
}
And from memory:
template<typename T> std::pair<T, bool> DeserializeMapFromXMLInMemory (std::stringstream map) {
try {
cereal::XMLInputArchive archive(map);
Map map;
archive(map);
return std::make_pair(map, true);
} catch (std::exception &ex) {
return std::make_pair(ex.what(), false);
} catch (...) {
return std::make_pair("Unknown error", false);
}
}
Now, some explanations:
The templates for both of these functions isn't required, but allows you to check the .second value of the pair and determine its truthfulness; if its true, then you know that what's in .first is a stringstream containing map data. But if its false, you know that you've got an error.
The difference between these two functions is that when you serialize something to a format like XML or JSON in memory you're returned the raw, serialized data that you can do with what you like (i.e. send it over the network). The file serialization/deserialization is designed to be saved in something like cold storage, then loaded sometime later in the future. The file at that point may or may not be on the same machine it was saved from. Either way, such a thing doesn't matter.
And, of course, the serialized data shouldn't be modified or read by humans unless you know what value is what.
"On two occasions I have been asked [by members of Parliament!]: 'Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out ?' I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question." — Charles Babbage.
My Github