Jump to content

  • Log In with Google      Sign In   
  • Create Account

noizex

Member Since 04 Jul 2011
Offline Last Active Yesterday, 04:24 PM

Topics I've Started

Sharing modules (but not globally)

15 January 2016 - 07:12 PM

Hello,

After looking at it for a while, I think the title of the topic may be a bit misleading, sorry. Basically I need a way to add some new code to already existing and compiled module or make two+ modules "see" each other as if they were one module. I tried searching the forums and I couldn't find a similar case. I know about shared entities, and I read the section from documentation:

 

While modules are independent, it may sometimes be necessary to exchange information between them. This can be accomplished in many different ways. One way is through function binding, where one module imports functions from another module and call those directly. Another way is with shared script entities, where handles to objects or functions can be exchanged between the modules. A third alternative is through messaging or proxy functions exposed by the application.

 

but I still can't figure that one out. So let me explain the structure I have and what I want to achieve. I have a GUI system that works with "overlays" - each overlay is self-contained and isolated piece of interface. It consists of a single Angelscript file that should define 2 methods: Init() for start-up work, and Update() for ticking. So let's say I have:

inventory.as
=========================================================================================
Context@ context; <---- this is global appended to every overlay script by C++ code

void Init()
{
   Document@ inv = context.loadDocument("inventory.rml");
   inv.show();
}

inventory.rml
=========================================================================================
<script>
   void do_something()
   {
      context.doSomethingWithContext();
   }
</script>
<input onclick='do_something()'>clickme!</input>

Now what happens when I create overlay from "inventory.as" is - it loads script from that AS file and compiles it as a module under the name of overlay (in this case "inventory"), it also appends a little global "Context@ context" to every such module. Then it calls Init() function to call script code to initialize logic, which in turn loads GUI document and shows it. But the problem is - this GUI document often contains snippets of embedded AS code like in the above example between tag "script" (it's based on JavaScript idea in HTML so that's how it works). And as the main module has already been compiled, I can't compile it again with the same name, and "append" the code. What is even more complicated, I have inline events like that onclick event, that also need to be in the same scope - right now it works because I call CompileFunction() on asIScriptModule (as it's just a single function, not some piece of code).

 

What I'd like to achieve, is to end up with the compiled module that has following code:

module "inventory"

____main_section

Context@ context;

void Init()
{
   Document@ inv = context.loadDocument("inventory.rml");
   inv.show();
}

____dynamically_loaded_script_section
void do_something()
{
context.doSomethingWithContext();
}

____dynamically_loaded_inline_section_0
void InlineFunction(Element@ this) { do_something() }

Is this even possible in Angelscript? The biggest problem with script section is that it can be anything, class definition, variables, function calls etc. Just any AS code. So either I make something shared (can I share whole module? without prepending everything with "shared"?) or compile it all together, which is impossible because when I create the module for the first time I have no knowledge what other documents with script code will be loaded (as it's dynamic). 

 

Is there any way to, say, make two or more modules "interconnected" so they appear "shared" to each other? Like some kind of linking, so instead of recompiling whole source from the scratch (also probably impossible because I can't really afford loosing or recreating whole UI script whenever it loads new document) I can just link pieces together and they'll see each other?

 

I hope the case is explained well enough, if something is unclear - let me know. I know that most obvious solution is probably remove these embedded scripts and force all logic into main script file (in this case inventory.as) but I'd rather try some other way if there is a chance to make it work the way I initially wanted.


Classes, namespaces, singletons

03 January 2016 - 08:07 PM

I recently went through source code of a certain MMO game, that was shut down some time ago, in a process of learning how "big guys" do things. I did so with several open sourced MMOs like Planeshift or Ryzom, but this time my brain melted due to some coding concept I encountered there. I will return to this shortly, but first I will describe my case:

 

In last few years I worked on an online open world game (no, not massive, just more than usual multiplayer mode for normal games). As it's a way more complex project than usual Tetris game, this means it contains a lot, and I mean a lot of various systems and subsystems that talk to each other. In this project the biggest challenge for me so far is designing classes and their interaction so it makes sense and doesn't create monolithic mess that can be found even in small projects. 

 

And more and more I notice, that I'm mostly making a class-to-be-instantiated-just-once that solves some problem as a system, and this class operates on few or more other classes that are actually instantiated in bigger amount. I bet that 80% of my code consists of classes that are never instantiated more than once (systems, services, graphics, game core etc.). I read tens of articles about singletons, service locators and all that, and I'm still confused what is really the right way to do this in really big ass project. And then I see this MMO code, easily thousands of classes, server/client/shared code. I look at the client initialization code and see chain of calls like this:

Graphics::init()
Sound::init()
Terrain::init()
Threading::init()
SkeletalAnimation::init()

... and so on, sometimes with parameters, sometimes not. These systems follow the same chain with their subsystems and deeper and deeper. I look at some code of such subsystem and it turns out it's not a real class, not even a singleton which I expected in the first place (usually syntax like Class::init() smells like some singleton initialization). All its methods are static, and it doesn't contain any members but it defines a namespace like TerrainNamespace inside class file where it puts some variables and accesses them. There is no object instantiation for these "classes", just some initialization and later shutdown, and of course hundreds of static functions that do things (and sometimes alter the state variables contained in namespace). Below some conceptual example of this:

MySystem.h

class MySystem
{
   public:
 
   static void Install();
   static vector& GetData();
   static void SetFlag(bool flag);

   private:

   static void LoadMyData();
}


MySystem.cpp

namespace MySystemNamespace
{
    int someVar;
    bool someOtherVar;
    std::vector someData;
}

using namespace MySystemNamespace;

void MySystem::Install()
{
    LoadMyData(); // loads data into someData vector
}

vector& MySystem::GetData()
{
    return someData;
}

void MySystem::SetFlag(bool flag)
{
    someOtherVar = flag;
}

so this is used like this:

void Loop()
{
    vector data = MySystem::GetData();
    doSomething(data);

    MySystem::SetFlag(1)
}

main()
{
    MySystem::Install();

    while (running)
      Loop();
}

I'm really shattered, because I've never expected to see something like this (and to such extent and of this magnitude) in one of the biggest gamedev efforts which is making a commercial MMO game. 

 

And the point of my post is, hopefully, stirring some discussion about this solution and finding an answer what is the correct way to design and develop big games with client-server architecture that have really a lot of things to do. 

 

I admit, I put that singleton in title intentionally ;) But this isn't really about singletons, but basic concepts of programming - the way above code works - it allows access to every functionalit of every subsystems without any guards. You want something done - just include a file and SomeSystem::doSomething() and it will be done. It seems tempting, because with so many interactions between systems, it starts to be daunting task to separate concerns, avoid coupling and so on. And here, it's all at my hand. I'm not even sure this can still be called OO-programming.

 

I wouldn't probably be so shocked if it was used in one or two places, but after exploring that code I'm convinced all systems that follow the rule of "probably no more than one instance of this class is ever needed in a program" are made in this manner. 

 

Hope to get some answers that will put me back on track, because right now I'm really confused.


How to send input data

16 June 2014 - 05:22 PM

I know there are many posts on this topic, but none of them answered the problems I have, or I don't have enough knowledge to comprehend them. I have client/server architecture, with server being authoritative. I implemented fixed timestep "physics" (for now its just moving) on both, client and server. I read about clien side prediction and server reconciliation, and I think I understand these concepts. What I don't get is how should input be handled. I read many posts about de-jitter buffers, packing many input samples into single packet and so on. But I never found a straightforward explanation how input plays with simulation.

 

I use RakNet, so I have quite high level networking lib that does all the magic for me like packing many similar messages into single packet, it has reliable UDP layer if I need it, I can have it ordered, reliable, unreliable etc. so its not a problem.

 

What I don't get is - when do I actually sample and send input data? Client ticks simulation at some rate, server ticks at another (but I guess its usually the same tick rate, right?). I use events for input so when key forward is pressed on the client, I receive event and set player state to "moving forward" and on next simulation step I move him forward one time (according to the current state). But when should that information be sent to the server? Should I send input state every simulation step? At the beginning? At the end? At different rate? I tried using different rate (like 20 input updates each second) but then client and server are getting out of sync because client logic sometimes does more steps (for example, pressing forward and releasing it quickly allowed for one simulation step on client, but server never received that input, the same for releasing key after some time - server did less steps than client). 

 

If I send input every simulation frame and stick some frame id to it I should be able to have it synced but what if input stops arriving at the server (packet loss or something)? Does the server keep with the information it had on previous tick, or it does nothing? I mean, if player holds "FORWARD" do I move player forward on server until message arrives that says key is no longer held? Or rather other way around - I move player only if the server receives input tick that says to move player forward? First case will move player even if he stopped holding forward for the time until late packets arrive (if they do), second will stop player even if it should be moving (because packets didn't reach the server). What solution is most commonly used and why? 

 

So, to reiterate - I have big problem understanding how simulation ticks on client/server work with client input - most tutorials I read never mention how input is actually sampled and sent to the server, they just mention it is and explain in more detail problems with lag compensation. I'd like to use server reconciliation but I can't until I understand how to mark input, keep it on client and still be able to refer to it when server sends update (that would be tied to some input it received, otherwise I won't be able to confirm it on client-side).


Communication with C++ from script side

25 January 2014 - 04:45 AM

Hello,

 

I'm trying to figure out the best way for flexible communication between script side and C++ side. What are you using when you need to call some specific C++ object method from AS script without registering it through script engine? 

 

I have event system, but it rather works in other direction (C++ sending events to Angelscript) and I'd like to keep it that way (one sided).

 

Lets consider situation where I have LoginState C++ class that handles logging logic and owns GUI overlay thats scripted in AS. In that script, I gather inputs, validate them and should call C++ method to send username/password through network interface. I don't want to expose network interface to scripting, but I'd like to have Auth(login, password) method available on script side.

 

Is registering LoginState as a OBJ_REF | OBJ_NOHANDLE type with a method Auth(login, password), then setting global property "LoginState state" the only way? Its a bit problematic with how script interface has to be registered, I'd need to register all possible methods prior to launching the state. 

 

Or I should invent some event system that works both sides? Where script can do "eventSystem.fire("auth", dict())" where dict has login/password and some method on C++ side listens for such event? I don't like event systems much, I use libRocket events for UI side, so I can launch some events from C++, but I'd rather keep it that way, because it fires events in UI widgets, and I want to keep them script-only so I don't even query them on C++ side. This means I'd need another event system for AS->C++ communication.

 

Maybe someone uses different methods to call some function with parameters on C++ side from AS?

 

PS. In case someone wants to discuss it live, I'm on #angelscript irc channel (@freenode) 24/7, my session hangs there but when I'm not afk I look there from time to time.


Network message solution for client/server architecture

16 January 2013 - 04:27 PM

Hello,

 

What are most common approaches for solving network messaging issue for client/server architecture (more towards online RPG game than first person shooter, so amount of messages is much bigger because there is a lot more going on in the world)? By message I mean communication between client and server (back and forth).

 

First method - Message classes

 

So far I explored Ryzom and Planeshift MMOs sources and they seem to solve this by having a shared code between client and server that has a Message class that is then inherited by every message (ClientLoginMsg, CharacterSelectMsg, GetInventoryMsg). Obviously each message will be used in two ways - the sender will serialize message into bitstream thats sent over the network, and receiver will deserialize it into something usable (class members, data structures etc.). So it looks a bit like this in pseudo-code:

 

class Message
{
}

class LoginAuthorizeMessgae: Message
{
    string user;
    string pass;
    BitStream out;

    // Assume that BitStream has already MESSAGE_ID read from it, so all thats left is message content
    // Because we read MESSAGE_ID, dispatcher knows which message to instantiate and it can be sent to
    // proper handlers
    LoginAuthorizeMessage(BitStream* in)
    {
       in.read(&user);
       in.read(&pass);
    }

    LoginAuthorizeMessage(string username, string password): user(username), pass(password);
    {
       out.write(MSG_AUTHORIZE_ID);  // write message id
       out.write(user);
       out.write(pass);
    }
}

// Then, client side:

void SendAuthRequest(login, pass)
{
    LoginAuthorizeMessage msg(login, pass);
    dispatcher->Send(msg);
}

// Server side:

void ReceiveAuthRequestHandler(Message* msg)
{
    LoginAuthorizeMessage* msg = (LoginAuthorizeMessage*)msg;
    if (db->auth(msg->user, msg->pass)
    {
       // ...
    }
}

 


Problem with this approach seems to be insane number of classes that need to be maintained. Just creating a simple response message with one text field requires declaring class with 2 constructors, then its .cpp file etc. Pros of this method is that same messages can be used by both, receiver and sender, so the code can be shared between client and server. All the serialize/deserialize logic is in one place, so its easier to maintain and harder to make some mistake because you see code for both, serialization and deserialization.

 

I came to even more advanced solution than the one above - I have a factory class that can create an instance of a correct class based on MESSAGE_ID, then I have an event dispatcher that is able to send that exact instance to receivers. A bit of a code snippet to demonstrate:

 
// =================
// NetworkManager
// =================

NetworkManager()
{
   m_MsgFactory->Register<SampleMessage>(NetMessageType::MSG_SAMPLE);
}

NetworkManager::HandlePacket(Packet* packet)
{
   msgID = GetPacketMsgId(packet);
 
   // Create a class thats registered for a given msgID
   // - for example for MSG_SAMPLE it will instantiate SampleMessage class and pass
   //   bitstream from packet.data into its constructor

   NetMessage* msg = m_MsgFactory->Create(msgID, packet.data);

   // This call sends that message to methods that are registered to handle it
   // I register methods instead of msgID, so the method is called with exact
   // class that it needs (so SampleMessage, instead of Message). It doesn't 
   // require explicit casting in handler method)

   m_MsgDispatcher->DispatchEvent(msg);
}

// =================
// SampleMessage
// =================

class SampleMessage: public NetMessage
{
public:
   int a;
   float b; 
 
  SampleMessage(RakNet::BitStream& msg): SampleMessage()
   {
      msg.Read(a);
      msg.Read(b);
   }

   SampleMessage(): NetMessage(NetMessageType::MSG_SAMPLE)
   {
      // This sets MSG_SAMPLE id for this message, so we 
      // dont have to care about it later
   }
};

// ====================
// SampleMessageHandler
// ====================
bool Init(NetMessageDispatcher& dispatcher)
{
    dispatcher.Subscribe(this, &SampleMessageHandler::HandleSampleMsg);
    return true;
}

void HandleSampleMsg(const SampleMessage* msg)
{
    std::cout << "Received sample message: " << msg.a << "\n";
} 

This works really nicely, but its probably a bit slower than some direct method (that clever event dispatcher that sends exact instance instead of passing Message* which allows me to register methods that receive directly what they want is probably doing some casting that could be avoided, although these are static_casts). Also it doesn't help with a problem of enormous amount of very small classes that may be hard to keep in order. 

 

Second method - sendXX methods to serialize some data to bitstream, and directly reading from message in receiver

 

This is method I've seen in some SWG emulator, but it was emulator and I only saw server code, so don't know how well it would work with client and if it was shared in any way or it was separate. In that code, there was a huge MsgLib.h file that defined a lot of methods like:

 


void sendPlayerPosition(Player*, Client*)
void sendServerTime(double time, Client*)
void sendInventory(Inventory*, Client*)
void sendChatMessage(message, channel, Client*)

and so on. Each of this methods serialized required data into a network message (msg.addUint32, msg.addString, msg.addFloat etc.) and sent it directly using provided Client.

 

void HandlePlayerPosition(Message* msg)
{
   msg.getFloat(&player.x);
   msg.getFloat(&player.y);
   msg.getFloat(&player.z);
}

 

This means that writing and reading happens in totally different places (server or client) so code is not really shared, all thats shared is opcodes or message ids because both sides need to be aware of them. 

 

 

The question

So, the question is, are there other methods? Are methods I described good enough and what can be done better? Am I doing something really wrong here? I find it hard to gather any knowledge on this topic, there seem to be no books on this, and only help were source codes of these three MMOs I mentioned at the beginning.

 

Thanks for any input! It would be very interesting to see how others solve this.

 


PARTNERS