Thursday, November 23, 2017

RPG: Map

Unfortunately the idea of using real world photos for a RPG was short lived. I continued for another couple weeks but the shortfalls just too heavily outnumbered the benefits.

Biggest upside of the photos was the beautiful static images the game could have, but you want the world to feel alive. I did have some limited success with rain effects and varying the lighting to give a little bit more movement into the world, so that probably could have been sufficiently resolved. However I had 2 problems that could not be overcome...

1. If I wanted to setup a shop, inn, or whatever fantasy style building, I had to hope it already existed on the trails (which it wouldn't be). Otherwise it would be a huge amount of photoshopping to add a building realistic enough to blend into the scene to not just one photo but several photos at a variety of angles and distances (with vegetation / scenery blocking parts of the building)..

2. Movement had to be in discrete steps. (In my prototype I had taken photos roughly every 20 feet, which is fine for turn based but not for real time.) And trying to merge multiplayer and turn based in what is essentially a first person dungeon crawler just doesn't work out. My son can play Diablo style games, so if I want him to enjoy the game with me I need something real time.

And thus, the game has been redesigned! Thankfully not much code was thrown out. In fact what the game is now is what I had originally played around with when first learning SFML several years ago. So I replaced the real-world location code with the "whole world" map code I had written before.

It looks much more like a modern action role playing game:


Please forgive my awful developer art. I'm focusing on code now!

You can walk around with WASD. Unexplored areas are completely gray, but there is a fog of war for areas you've previously explored (shaded darker). Characters such as yourself, trees, or buildings can be on the game map. Also equipment can be dropped onto or picked up from the map. I have draw order working nicely - you can walk behind trees for example. The game prevents you from walking through trees (based on a radius) or buildings (based on a width/depth).


In the above screenshot, I've picked the sword up off the ground. I can now equip it or put it in my backpack.

You can use the mouse scroll wheel to zoom in or out of the map.


You can actually zoom out enough to see the entire world...


A player can traverse the entire globe. For the past several weeks I've been steadily making improvements, and I'm really liking the results. Terrain tiles and characters are animated. (Water can have waves, trees can sway in the breeze, and the player's feet move as you walk around.)

At this point I've got a very solid plan for the game.

The 5 second pitch is: Growing up you had the dream of mapping the entire world. And now that you're older, you've decided to do just that. But quite a lot of things can happen to someone exploring a whole world...

So the scope has increased, but a playable version shouldn't take too much longer. I can create a world with monsters to fight, inns to stay at, etc. relatively quickly. But this also gives me room to go much further. I could have warring kingdoms that you could join sides in or try to resolve in other ways. I could have the classic evil mage in the tower threatening nearby lands you may want to defeat. I could have a small farm community that you run across and decide is where you want to put down your roots. (Inspired by Stardew Valley...) But I don't want to get into too much scope creep yet; for now it's build a world you can run around in and kill monsters & improve yourself.

Tuesday, October 24, 2017

Rocket Launcher


Launching Estes rockets is fun; really fun. When I was younger I had a proper launch setup with rocket stand and handheld launcher. At some point those got lost and/or thrown out. The past few years to launch a rocket I just grabbed a long coil of wire and a 9-volt battery. Connect the wire to the Estes igniter, hold the battery against the wires, and lift off!

I'd like my son to have a slightly more... authentic(?) experience.

For the launcher, I want a nicely packaged box, with "ready" indicator light, launch button, and most importantly a safety key switch. That way I can hold onto the key while hooking the wires up to the igniter (without worrying about my son pressing launch prematurely).

Other than the key switch, all the other parts were leftovers from prior projects.


Drilled the holes for the key switch, light, and push button.


Quick and easy crimp the wires together. (There is a hole in the side for the long wire that goes out to the rocket.)


Wiring diagram for those that can't follow the above mess...


When the key is turned, the light illuminates signalling that the rocket is armed. Press the red button and electricity goes out to the igniter to launch.


And it works! There was basically no wind so it landed feet from the launch site.


Next up is a proper launch stand!

Friday, September 29, 2017

RPG: Intro


I wanted to make a follow on to my Pebble watch RPG, but since the company is now gone that would not be the best use of my time. I still however have a desire to make the kind of RPG I want to play. And during a nice hike with the family one day, I decided on the type of RPG to make!

Two RPGs that I spent by far the most time on and enjoyed the most are Diablo 1 and a MUD called Dreamshadow. I played through Diablo 2 and 3, but I think the first one is best. (Please note, my opinions on Diablo 3 are before Loot 2.0 so I understand they fixed many of the issues with the PC version of the game.)

Customization
In Diablo 1, you pick from one of three classes which bounds your stats, but you really have complete freedom beyond that. Your rogue can cast spells and your mage can wield swords. Straying from your initial class definitely puts you at a handicap, but it's still an option.
In Diablo 2, you have to pick a development path for your character and that's it. To even experiment with other abilities of the same class you have to create a new character.
In Diablo 3, well, there is no customization. You have what everyone else will have at your level.

Death
In Diablo 1, you have actual tense moments because of the consequences of death. (You really don't want the butcher to kill you, because then you have to go back down there to fight him naked to get your stuff back - at least in multiplayer mode.)
In Diabo 2, it's very similar but I remember you being able to re-equip yourself in a single click rather than Diablo 1's managing items one by one.
In Diablo 3, well, you come back to life a trivial distance away that you can usually walk back to in about 10 seconds. A minor inconvenience.

Items
In Diablo 1, getting a magical item was a big deal. That was excitement. And not only that, you absolutely had to identify that item before using it because it could be cursed and make you worse.
In Diablo 3, well, freaking everything drops a mountain of magical items. And it's so bad excessive are multiple level of magical items such as rare and legendary. After killing some monsters it's a chore sifting through which magical items are even worth messing with. And were there even any cursed magical items?

Levels & Skills
The Dreamshadow MUD did not use levels. Instead, it had this fantastic hierarchy based skill system that was both very realistic and very rewarding. I'll go into those details when I code it, but it is a must have.

So with my game, I want your character to have no arbitrary limits. You can be as well rounded or specialized as you want. With death, I don't want people to rage quit, but I also want them to really NOT want to die. And finally a magical item is something especially rare and special. I have many other design ideas for the game, but I'll go into those details as I address them in the code.

I like an isometric view over a first-person view in RPGs, but being on the hike game me an interesting idea. Rather than going with retro 8-bit graphics or fancy 3-D generated worlds, my RPG could consist of hundreds of actual photos of real-world places. Basically the adventurer will be fighting in real world areas - along actual state and national park trails. Most hiking trails don't have too many branches that go off of them, but I think my Pebble game was still successful where you had no ability to veer off the single path anyways. So perhaps it could work.


I went along a nearby state park to take many pictures in various directions. For a proof of concept, I just took pictures with my phone camera, that has a scratch over the lens, in one hand, while carrying my toddler in my other arm, while said toddler was trying to reach for said camera. It's a prototype...

A few things were evident right away... need to take pictures at high noon, might need to take pictures when it's overcast, and the locations & orientations of each photo need to be recorded immediately upon taking them. All things that can be addressed on a second outing to the park if the prototype game looks worth pursuing.

One important piece is I want to make the still pictures feel as alive as possible. I'm experimenting with graphical effects to accomplish that.

I've got the torch effect when it gets dark looking pretty good... (intensity flickers)


And I just implemented some rain... (looks better animated)


I'm using SFML (Simple Fast Media Library) as my only third party library right now. It should cover everything I need. I don't have any code that I want to post now, but I'm writing my own presentation library to handle the drawing and user controls. It's coming along nicely and will likely be valuable to others when it's ready to share.

Whereas the Orbital Aero is a very long term multi-year project, I'd like to have a playable version of this "Trail RPG" within a few months. (Honestly it's not that development takes that much time, it's just my limited time to do it.) So Orbital Aero is still in active development! But I'll be bouncing back and forth between it and this game.

Wednesday, August 30, 2017

Orbital Aero Model: Kinematics (Rotation)

In the first Kinematics post, I went into how bodies are moved in space. This post will go into how bodies are rotated in space.


The amount a body will rotate is not based on force and mass, but instead it is based on the moment applied to the body (the Torque, T) and the body's moment of inertia (I).

Torque


https://en.wikipedia.org/wiki/Torque


Torque (moment) is calculated from the equation:


T is the calculated torque applied.
r is the perpendicular distance from the axis of rotation to the force applied.
F is the force applied.

A simple way of thinking about moment is a wrench tightening a bolt.

If the wrench was 1 foot long with 10 pounds of pressure applied to the end of it, then the calculated moment is:
T = 1 * 10 = 10 foot-pounds of torque

If the wrench was twice as long but the same force applied, then the calculated moment is:
T = 2 * 10 = 20 foot-pounds of torque

Which demonstrates the entire purpose of having long handles on wrenches! They allow people to tighten bolts with less force.

Since the entire simulation (and most of the world) uses metric, I'll be using metric for the rest of this post and the code. So our moment units will be Newton-meters rather than foot-pounds.

For a metric example the wrench is 0.25 meters long with 50 newton of force applied, the calculated moment is:
T = 0.25 * 50 = 12.5 Newton-meters of torque

The torque equation I gave above was just for a single axis plane. The equation to cover a vector force in multiple dimensions uses the cross product:


This equation takes a vector force, F, and a vector position, r, to get the resulting torque, T, that is applied in all 3 axes.


In this diagram, a force vector, F, is applied at the corner of a block with the force location represented by vector r. The torque cross product equation gives the resulting torque in all 3 axes.

For more information on the cross product, see https://en.wikipedia.org/wiki/Cross_product The Vector class has a static function for calculating the cross product which the simulation code below will use.

Moment of Inertia


https://en.wikipedia.org/wiki/Moment_of_inertia

All physical objects have a mass, and all physical objects have a moment of inertia. Where mass is an object's resistance to movement, moment of inertia is an object's resistance to rotation.

And where mass is the same from any orientation, the moment of inertia can vary from orientation. There are simple formulas to calculate moment of inertia for a variety of shapes:

Uniform Solid Sphere (moment of inertia same from any orientation):




Block (moment of inertia varies by axis):





https://en.wikipedia.org/wiki/List_of_moments_of_inertia

What happens if you have a more complex shape? (Two spheres at the end of a long block.) Or you're rotating somewhere other than the axis that the moment of inertia equations were referenced from? (Rotating from the end of the block rather than the center.) In those situations, you use the Parallel-Axis Theorem to recalculate a new moment of inertia.

https://en.wikipedia.org/wiki/Parallel_axis_theorem

Currently in the Orbital Aero Model, all bodies are single uniform spheres. I have plans to make it so bodies can consist of one of a variety of basic shapes or a combination of many basic shapes to form complex shapes. So I'll hold off on covering the parallel axis theorem until I complete that code.

Angular Acceleration & Velocity


https://en.wikipedia.org/wiki/Kinematics

In our prior function calls, we calculated the total moment applied to the body. Since we know its moment of inertia, it's easy to calculate acceleration:


T is the torque, in newton-meters.
I is the moment of inertia, in kilogram meters squared.
alpha is the angular acceleration, in radians per second squared.

And with the angular acceleration, we can rotate the object:


theta is the newly calculated orientation, in radians.
theta0 is the initial orientation, in radians.
omega0 is the initial angular velocity, in radians per second.
alpha is the angular acceleration, in radians per second squared.
t is the time step of the simulation, in seconds.


omega is the newly calculated angular velocity, in radians per second.
omega0 is the initial angular velocity, in radians per second.
alpha is the angular acceleration, in radians per second squared.
t is the time step of the simulation, in seconds.

The smaller the time step the more accurate the simulation. However, that also means it's more computational intensive for simulating the same duration of time.



Orbital Aero Code


Now that all the background math has been covered, let's see how it's represented in code.

Body


I added a moment of inertia property to the Body class. Since everything in the orbital aero are considered uniform spheres for now, this returns a moment of inertia based on the body's mass and radius.


class Body
{
public:
    Vector momentOfInertiaTotal_kilogramMeters2() const;
.
.
.
}

Vector Body::momentOfInertiaTotal_kilogramMeters2() const
{
    // For solid spheres: I = 2/5 * M * R^2
    double momentOfInertia_kilogramMeters2 = (double)2.0 / (double)5.0 * this->massTotal_kilograms() * (this->radius_meters * this->radius_meters);
    return Vector(momentOfInertia_kilogramMeters2, momentOfInertia_kilogramMeters2, momentOfInertia_kilogramMeters2);
}

Control System


I'll demonstrate 2 ways of calculating and applying a moment to a body.

Reaction Wheel / Directly Calculate Moment from Joystick Deflection


This approach would be more for satellites with reaction wheels. Rather than expelling force via a rocket to rotate the vehicle, an electric motor attached to a flywheel spins causing the vehicle to counter-rotate. https://en.wikipedia.org/wiki/Reaction_wheel

Of course we could go in depth with the physics of the motor and flywheel, but for simplicity we can just scale the joystick input to a torque. And we can use the body's moment of inertia as a general rule for how much to scale the joystick input.


bool ControlSystemModel::update(OrbitalAeroModel &hostOrbitalAero, Body* hostBody, double timeStep_seconds)
{
  switch (controlInput)
  {
  case ControlInputEnum::idle:
  // No change to any angular velocities set.
  break;
  case ControlInputEnum::joystick:
  {
            // Scale the moment applied by the moment of inertia so our joystick inputs are effective for both
            // very small and very large bodies. This will result in 1 radians/second^2 for full joystick deflection.
            Vector momentOfIneretiaTotal_kilogramMeters2 = hostBody->momentOfInertiaTotal_kilogramMeters2();
            hostBody->momentTotal_newtonMeters.x = hostOrbitalAero.joystickInputs[this->joystickInputsIndex].roll * momentOfIneretiaTotal_kilogramMeters2.x;
            hostBody->momentTotal_newtonMeters.y = -hostOrbitalAero.joystickInputs[this->joystickInputsIndex].pitch * momentOfIneretiaTotal_kilogramMeters2.y;
            hostBody->momentTotal_newtonMeters.z = hostOrbitalAero.joystickInputs[this->joystickInputsIndex].yaw * momentOfIneretiaTotal_kilogramMeters2.z;

            // All thrust is along the x (forward) axis of the entity.
            double thrust_newtons = hostOrbitalAero.joystickInputs[this->joystickInputsIndex].thrust * 1000.0;

            // Convert thrust from local axis to world axis.
            Vector thrustBody_newtons(thrust_newtons, 0, 0);
            hostBody->forceThrust_newtons = thrustBody_newtons.rotatedBy(hostBody->orientation_quaternions.inverse());
      }
         break;
  default:
  {
            this->hostBody->momentTotal_newtonMeters = 0.0;
            this->hostBody->forceThrust_newtons = 0.0;
      }
  break;
  }

  return true;
}

Orientation Thrusters / Calculate Moment from Rockets at Edges of Body


This approach is more inline with the Space Shuttle's reaction control system. https://en.wikipedia.org/wiki/Reaction_control_system A combination of small thrusters on various parts of the spacecraft are fired for attitude adjustments. (They could also make translation adjustments if opposing thrusters are not fired to counteract the force applied.)

The code below models 6 thrusters for orientation control. There is a pair for each axis; one on either side of the body. For any thrust created on one side, a thrust in the opposite direction is created on the other side so that the body only rotates (no translation).

These thrusters pretend they can create force in either direction, which in reality you wouldn't do. (Rocket nozzles point in one direction, not two.) Really this is 12 thrusters with math simplified for 6. That's not many though, the Space Shuttle had 44 of these! (But those were also used for minor translation changes and as backups in case of malfunction.)


bool ControlSystemModel::update(OrbitalAeroModel &hostOrbitalAero, Body* hostBody, double timeStep_seconds)
{
  switch (controlInput)
  {
  case ControlInputEnum::idle:
  // No change to any angular velocities set.
  break;
  case ControlInputEnum::joystick:
  {
             // Orientation Thrusters

            // Roll 1 Thruster - Right Wingtip, pointed up/down.
            Vector roll1ThrustersForce_newtons(0.0, 0.0, hostOrbitalAero.joystickInputs[this->joystickInputsIndex].roll * 10.0);
            Vector roll1ThrustersLocation_meters(0.0, hostBody->radius_meters, 0.0);
            Vector roll1ThrustersMoment_newtonMeters = Vector::crossProduct(roll1ThrustersLocation_meters, roll1ThrustersForce_newtons);

            // Roll 2 Thruster - Left Wingtip, pointed up/down.
            Vector roll2ThrustersForce_newtons(0.0, 0.0, hostOrbitalAero.joystickInputs[this->joystickInputsIndex].roll * -10.0);
            Vector roll2ThrustersLocation_meters(0.0, -hostBody->radius_meters, 0.0);
            Vector roll2ThrustersMoment_newtonMeters = Vector::crossProduct(roll2ThrustersLocation_meters, roll2ThrustersForce_newtons);

            // Pitch 1 Thruster - Nose, pointed up/down.
            Vector pitch1ThrustersForce_newtons(0.0, 0.0, hostOrbitalAero.joystickInputs[this->joystickInputsIndex].pitch * 10.0);
            Vector pitch1ThrustersLocation_meters(-hostBody->radius_meters, 0.0, 0.0);
            Vector pitch1ThrustersMoment_newtonMeters = Vector::crossProduct(pitch1ThrustersLocation_meters, pitch1ThrustersForce_newtons);

            // Pitch 2 Thruster - Tail, pointed up/down.
            Vector pitch2ThrustersForce_newtons(0.0, 0.0, hostOrbitalAero.joystickInputs[this->joystickInputsIndex].pitch * -10.0);
            Vector pitch2ThrustersLocation_meters(hostBody->radius_meters, 0.0, 0.0);
            Vector pitch2ThrustersMoment_newtonMeters = Vector::crossProduct(pitch2ThrustersLocation_meters, pitch2ThrustersForce_newtons);

            // Yaw 1 Thruster, Nose, pointed left/right.
            Vector yaw1ThrustersForce_newtons(0.0, hostOrbitalAero.joystickInputs[this->joystickInputsIndex].yaw * 10.0, 0.0);
            Vector yaw1ThrustersLocation_meters(hostBody->radius_meters, 0.0, 0.0);
            Vector yaw1ThrustersMoment_newtonMeters = Vector::crossProduct(yaw1ThrustersLocation_meters, yaw1ThrustersForce_newtons);

            // Yaw 2 Thruster, Tail, pointed left/right.
            Vector yaw2ThrustersForce_newtons(0.0, hostOrbitalAero.joystickInputs[this->joystickInputsIndex].yaw * -10.0, 0.0);
            Vector yaw2ThrustersLocation_meters(-hostBody->radius_meters, 0.0, 0.0);
            Vector yaw2ThrustersMoment_newtonMeters = Vector::crossProduct(yaw2ThrustersLocation_meters, yaw2ThrustersForce_newtons);

            // Main thruster is at the tail along x (forward) axis of the entity.
            Vector mainThrustersForce_newtons(hostOrbitalAero.joystickInputs[this->joystickInputsIndex].thrust * 1000.0, 0.0, 0.0);
            Vector mainThrustersLocation_meters(-hostBody->radius_meters, 0.0, 0.0);
            Vector mainThrustersMoment_newtonMeters = Vector::crossProduct(mainThrustersLocation_meters, mainThrustersForce_newtons);

            // Get total thrust.
            Vector totalThrustBody_newtons = roll1ThrustersForce_newtons + roll2ThrustersForce_newtons + 
                                             pitch1ThrustersForce_newtons + pitch2ThrustersForce_newtons + 
                                             yaw1ThrustersForce_newtons + yaw2ThrustersForce_newtons + 
                                             mainThrustersForce_newtons;

            // Get total moment.
            hostBody->momentTotal_newtonMeters = roll1ThrustersMoment_newtonMeters + roll2ThrustersMoment_newtonMeters + 
                                                 pitch1ThrustersMoment_newtonMeters + pitch2ThrustersMoment_newtonMeters + 
                                                 yaw1ThrustersMoment_newtonMeters + yaw2ThrustersMoment_newtonMeters + 
                                                 mainThrustersMoment_newtonMeters;

            // Convert thrust from local axis to world axis.
            hostBody->forceThrust_newtons = totalThrustBody_newtons.rotatedBy(hostBody->orientation_quaternions.inverse());

         break;
  default:
  {
            this->hostBody->momentTotal_newtonMeters = 0.0;
            this->hostBody->forceThrust_newtons = 0.0;
      }
  break;
  }


  return true;
}

processKinematics()


Kinematics have been updated to calculate the angular acceleration from the applied moment and moment of inertia.


//  Translate and rotate bodies based on their mass, moments of inertia, and forces.
bool OrbitalAeroModel::processKinematics(double timeStep_seconds)
{
  // Loop through all bodies and reposition them based on the forces.
  map <uint64_t, Body*>::iterator iteratorPrimaryBody = tableBodies.begin();
  while (iteratorPrimaryBody != tableBodies.end())
  {
  Body* primaryBody = iteratorPrimaryBody->second;
  if (primaryBody->isActive && !primaryBody->isExternal)
  {
  if (primaryBody->isStationary)
  {
                // Body Stationary, No Movement
primaryBody->linearAcceleration_metersPerSecond2 = 0.0;
primaryBody->linearVelocity_metersPerSecond = 0.0;
                primaryBody->angularAcceleration_radiansPerSecond2.clear();
                primaryBody->angularVelocity_radiansPerSecond.clear();
  }
  else
  {
                // Translation
                primaryBody->linearAcceleration_metersPerSecond2 = primaryBody->forceTotal_newtons() / primaryBody->massTotal_kilograms();
                // Distance Travelled = (Initial Linear Velocity * Time) + (0.5 * Linear Acceleration * Time^2)
                primaryBody->location_meters += (primaryBody->linearVelocity_metersPerSecond * timeStep_seconds) + (primaryBody->linearAcceleration_metersPerSecond2 * (0.5 * timeStep_seconds * timeStep_seconds));
                // Final Linear Velocity = Initial Linear Velocity + (Linear Acceleration * Time)
primaryBody->linearVelocity_metersPerSecond += primaryBody->linearAcceleration_metersPerSecond2 * timeStep_seconds;
                
                // Rotation
                primaryBody->angularAcceleration_radiansPerSecond2 = primaryBody->momentTotal_newtonMeters / primaryBody->momentOfInertiaTotal_kilogramMeters2();
                // Rotation Amount = (Initial Angular Velocity * Time) + (0.5 * Angular Acceleration * Time^2)
                Euler rotationAmount_radians = (primaryBody->angularVelocity_radiansPerSecond * timeStep_seconds) + (primaryBody->angularAcceleration_radiansPerSecond2 * (0.5 * timeStep_seconds * timeStep_seconds));
                if (rotationAmount_radians.isNonZero())
                {
                    // Rotate around local axis.
                    primaryBody->orientation_quaternions.rotateAboutLocalAxis(rotationAmount_radians);
                }
                // Final Angular Velocity = Initial Angular Velocity + (Angular Acceleration * Time)
                primaryBody->angularVelocity_radiansPerSecond += primaryBody->angularAcceleration_radiansPerSecond2 * timeStep_seconds;
            }
  } // (primaryBody.isActive)
  iteratorPrimaryBody++;
  } // (iteratorPrimaryBody != tableBodies.end())

  return true;
}


Copyright (c) 2017 Clinton Kam
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Friday, July 28, 2017

Orbital Aero Model: TCP Network

I'm designing the Orbital Aero application to support multiple simultaneous types of network connections. The first implemented is a proprietary TCP connection. TCP will make it easier to establish and maintain connections over the Internet. And a proprietary network structures will give me maximum flexibility to replicate the exact same simulation across all connected users.

My class design is as follows:
- The Main Application contains the objects: Orbital Aero Library, Network, and Visual Model Library (among others, but I'm just talking networking for now).
- The Network contains the OANet object (in the future, other network protocol objects).
- The OANet object contains the TCPServer and TCPClient objects and references to the Orbital Aero Library (so it can add/remove bodies) and the Visual Model Library (so it can associate visual models to the newly created entities)..
- The actual networking code is in the TCPServer and TCPClient objects. Function callbacks relay packets back up to OANet so incoming entities can be created and updated.


It's working really well. Currently each entity is updating its position on the network every frame. I don't have any kind of dead reckoning / threshold enabled (yet) which would allow me to cut down the packets but still keep the simulation in sync. That will be a future post.

Here's a screenshot of the TCP network in action:


The left panel is the TCP server creating only the nearby Earth (using a Moon visual model). The center panel is a TCP client creating one of the distant Moons. The right panel is another TCP client creating a second Moon. But all of the bodies are interacting / influencing each other in the simulation.

I'm only supporting one packet type now, an Entity State which shares where the body is in the world. Just that packet is enough for gravity influences to work great. Collisions won't reliably work between the separate simulations yet. I need to create a Collision style packet such that if one simulation detects a collision that it gets updated the same across all the simulations.



Orbital Aero App


I'll be covering just the the new network-specific code below, but it will be easier to follow that way. The TCPServer and TCPClient code can likely be a straight copy-paste into your own application, and then you'll just need to create your own variant of the OANet/Network code. Honestly for most software there is no need to separate the logic in OANet and Network. I plan on supporting multiple network protocols so it made since for me to split them.


Entity.h


Now that the software can support both internally and externally generated entities, I need to keep track of who originated / is managing those entities. I've added a new source variable to my Entity class and timeOfLastUpdate_seconds. The timeOfLastUpdate_seconds is used to timeout entities that have gone stale on the network (likely network dropped out / remote software shut down).


#pragma once

class BodyModel;
class VisualModel;

class Entity
{
public:
Entity(void);
Entity(BodyModel *newBody);
~Entity(void);

BodyModel *bodyObject;

VisualModel *visualModelObject;

bool isVisible;

    enum class SourceEnum
    {
        Undefined = 0,
        Internal = 1,
        OANet = 2,
    };

    SourceEnum source;
    double timeOfLastUpdate_seconds;

private:

};


If the body is internally created, I do:
    newEntity->source = Entity::SourceEnum::Internal;

If the body is externally created from my OANet, I do:
    incomingEntity->source = Entity::SourceEnum::OANet;


OrbitalAeroApp.cpp


I'm including the new Network.h header inside the main app file.


#include ".\Network\Network.h"
.
.

int main(int argc, char* argv[])
{


For testing purposes, I ask the user at program start if this program is standalone, a server, or a client.


    bool isStandAlone = false;
    bool isOANetServer = false;
    bool isOANetClient = false;
    cout << "Network Type:" << endl;
    cout << "  0 Stand Alone" << endl;
    cout << "  1 TCP Server" << endl;
    cout << "  2 TCP Client" << endl;
    int networkInput;
    cin >> networkInput;
    switch (networkInput)
    {
    case 0: // Stand Alone
        isStandAlone = true;
        break;
    case 1: // TCP Server
        isOANetServer = true;
        break;
    case 2: // TCP Client
        isOANetClient = true;
        break;
    }


The server and all clients that connect need to have a unique identification for their own entities so they don't conflict with each other. To do this, I'm asking the user to enter a unique Site ID; all bodies created by that instance of the software will increment off that Site ID to prevent duplicate IDs. Ultimately I'll have the server assign an identification to clients as they connect, but this is quick and easy to start with.


    cout << "Site ID:" << endl;
    int siteID;
    cin >> siteID;


Each time a body is created, the Orbital Aero model pulls a new UInt64 value from a counter to uniquely identify the new body. Then the counter increments by 1 to be ready for the next new body. By initializing the counter to some large initial number based on the user's siteID, I can keep conflicts from happening (in all but very extreme situations). Ultimately I'll have a lower and upper bound for the indices so different networked simulations never step on each other.


    printf("Initializing Orbital Aero...\n");
    OrbitalAeroModel orbitalAero;

    orbitalAero.setIndexCounter(siteID * 100000);


The network is connected based on the options entered earlier. Be sure to have the server running before starting the clients.


    printf("Connecting Network...\n");
    Network network(&orbitalAero, &visualModelLibrary);
    if (isOANetServer)
    {
        network.startOANetServer(4000);
    }
    if (isOANetClient)
    {
        network.connectToOANetServer("127.0.0.1", 4000);
    }
    printf("- %lf\n", Timer::instance()->getElapsedTimeAndResetTimer_seconds(timerReference_seconds));


For testing I only create the Earth if it's a server, and I only create the moon if it's a client.


printf("Adding bodies...\n");
    if (isStandAlone || isOANetServer)
    {
        addBodyEarth(orbitalAero, &visualModelLibrary.planet);
    }

    if (isStandAlone || isOANetClient)
    {
        addBodyMoon(orbitalAero, &visualModelLibrary.planet);
    }


The main program loop (described in detail earlier)...


bool isRunning = true;
while (isRunning)
{
orbitalAero.update(0.2);
.
.


Inside the main loop after all the bodies have updated their positions, I update internally generated entities over the network so other simulations can have the latest positions. I also timeout externally generated entities that haven't been updated in a while (likely the network connection has failed.)

This update frequency (every frame) is very spammy on the network. The long term fix for this is called dead reckoning coupled with threshold breaking. Basically I should send the position of the entity along with its velocities, accelerations, etc. Receiving simulations can take the position I gave it along with the velocities and accelerations and continue flying out that entity without updated network packets. The local simulation keeps track of the true position of the entity and where other simulations think the entity is (based on the velocity/acceleration dead reckoning). When the truth and projected positions exceed a threshold, then the local simulation broadcasts a new packet to resync the entity across the network. Future code to write...


        // Loop through all bodies and update them on the network.
        map <uint64_t, BodyModel*>::iterator iteratorBody2 = orbitalAero.tableBodies.begin();
        while (iteratorBody2 != orbitalAero.tableBodies.end())
        {
            BodyModel *loopBody = iteratorBody2->second;

            // Increment iterator immediately, as the loopBody can be removed here which
            // would invalidate the iterator if it still pointed to a removed body.
            iteratorBody2++;

            if (loopBody->isActive)
            {
                Entity *loopEntity = (Entity*)loopBody->hostData;
                switch (loopEntity->source)
                {
                case Entity::SourceEnum::Internal:
                    network.updateEntityToNetwork(loopEntity);
                    break;
                case Entity::SourceEnum::OANet:
                    if (Timer::instance()->getElapsedTime_seconds(loopEntity->timeOfLastUpdate_seconds) > 1.0)
                    {
                        // Entity hasn't updated in over a second, time out.
                        orbitalAero.removeBody(loopBody->index);
                    }
                    break;
                }
            } // (loopBody.isActive)
        } // (iteratorBody2 != tableBodies.end())

Sleep(20);
}


Network


The Network class contains currently contains just my proprietary OANet. It includes functions so the application can start/stop the OANet server, connect/disconnect to the OANet server, or update an entity on the network.

Network.h



#include ".\OANet\OANet.h"

class Entity;
class OrbitalAeroModel;
class VisualModelLibrary;

class Network
{
private:
    // oaNet networking object.
    OANet oaNet;

public:
    // Network Constructor
    Network(OrbitalAeroModel* newOrbitalAero, VisualModelLibrary* newVisualModelLibrary);

    // Network Destructor
    ~Network();

    // Start the OANet server.
    bool startOANetServer(int port);

    // Stop the OANet server.
    bool stopOANetServer();

    // Connect to an OANet server.
    bool connectToOANetServer(char ipAddress[16], int port);

    // Disconnect from an OANet server.
    bool disconnectFromOANetServer();
    
    // Update an entity to active networks.
    bool updateEntityToNetwork(Entity* entity);
};


Network.cpp



#include "..\Entity.h"
#include ".\OANet\Packets\OANetEntityState.h"
#include "Network.h"

// Network Constructor
Network::Network(OrbitalAeroModel* newOrbitalAero, VisualModelLibrary* newVisualModelLibrary) :
    oaNet(newOrbitalAero, newVisualModelLibrary)
{
    return;
}

// Network Destructor
Network::~Network()
{
    return;
}

// Start the OANet server.
bool Network::startOANetServer(int port)
{
    return oaNet.startServer(port);
}

// Stop the OANet server.
bool Network::stopOANetServer()
{
    return oaNet.stopServer();
}

// Connect to an OANet server.
bool Network::connectToOANetServer(char ipAddress[16], int port)
{
    return oaNet.connectToServer(ipAddress, port);
}

// Disconnect from an OANet server.
bool Network::disconnectFromOANetServer()
{
    return oaNet.disconnectFromServer();
}

// Update an entity to active networks.
bool Network::updateEntityToNetwork(Entity* entity)
{
    if (oaNet.isServerActive() || oaNet.isConnectedToServer())
    {
        oaNet.sendEntityState(entity);
    }
    return true;
}


OANet


OANet is my proprietary network protocol for sharing entity information between simulations. It is based on a TCP Server <--> Client connection. A single Orbital Aero app acts as the server and any number of clients can connect to it.

OANet.h



#include "..\TCP\TCPServer.h"
#include "..\TCP\TCPClient.h"

class Entity;
class OrbitalAeroModel;
class VisualModelLibrary;

class OANet
{
private:
    // TCP Server and Client objects that contain the actual network code.
    TCPServer tcpServer;
    TCPClient tcpClient;

    // Reference to host orbital aero so the network can manipulate network created entities.
    OrbitalAeroModel* orbitalAero;

    // Reference to the visual library so visual models can be associated with incoming entities.
    VisualModelLibrary* visualModelLibrary;

public:
    // OANet Constructor
    OANet(OrbitalAeroModel* newOrbitalAero, VisualModelLibrary* newVisualModelLibrary);

    // OANet Destructor
    ~OANet();

    // Static function used with callbacks for when a packet is received from the network.
    static void callBackProcessIncomingPacket(void* hostNetwork, TCPServer::ConnectionModel* sourceConnection, char packetData[TCPServer::maximumPacketLength_bytes], int packetLength_bytes);
    
    // Function called when a packet is received from the network.
    void processIncomingPacket(TCPServer::ConnectionModel* sourceConnection, char packetData[TCPServer::maximumPacketLength_bytes], int packetLength_bytes);

    // Send an entity over the network.
    bool sendEntityState(Entity* entity);

    // Start a TCP server at a specified port.
    bool startServer(int tcpPort);

    // Stop the TCP server.
    bool stopServer();

    // Return if we're actively hosting a server.
    bool isServerActive();

    // Connect as a client to a server at a specified IP address and port.
    bool connectToServer(char ipAddress[16], int port);

    // Disconnect from a server.
    bool disconnectFromServer();
        
    // Return if we're a client connected to a server.
    bool isConnectedToServer();
};


OANet.cpp



#include "OANet.h"
#include ".\Packets\OANetEntityState.h"
#include "..\..\Entity.h"
#include "..\..\VisualModelLibrary.h"
#include "..\..\..\OrbitalAeroModel\BodyModel.h"
#include "..\..\..\OrbitalAeroModel\OrbitalAeroModel.h"
#include "..\..\..\OrbitalAeroModel\Euler.h"
#include "..\..\..\OrbitalAeroModel\Vector.h"
#include "..\..\..\OrbitalAeroModel\Quaternion.h"
#include "..\..\..\OrbitalAeroModel\ControlSystemModel.h"
#include "..\..\..\OrbitalAeroModel\PartModel.h"
#include "..\..\..\OrbitalAeroModel\BodyModel.h"
#include "..\..\Timer.h"

// OANet Constructor
OANet::OANet(OrbitalAeroModel* newOrbitalAero, VisualModelLibrary* newVisualModelLibrary) :
    // Initialize tcpServer and tcpClient variables.
    // Pass in functions callbacks for when packets are received from the network.
    tcpServer(this, OANet::callBackProcessIncomingPacket),
    tcpClient(this, OANet::callBackProcessIncomingPacket)
{
    // And hold references to the orbital aero and visual model library in this OANet object.
    this->orbitalAero = newOrbitalAero;
    this->visualModelLibrary = newVisualModelLibrary;

    return;
}

// OANet Destructor
OANet::~OANet()
{
    return;
}

// Static function used with callbacks for when a packet is received from the network.
void OANet::callBackProcessIncomingPacket(void* hostNetwork, TCPServer::ConnectionModel* sourceConnection, char packetData[TCPServer::maximumPacketLength_bytes], int packetLength_bytes)
{
    // Packet received from the network!
    // Interpret the hostNetwork pointer as the OA Network, then call its processIncomingPacket function.
    OANet* oaNet = (OANet*)hostNetwork;
    oaNet->processIncomingPacket(sourceConnection, packetData, packetLength_bytes);
    return;
}

// Function called when a packet is received from the network.
void OANet::processIncomingPacket(TCPServer::ConnectionModel* sourceConnection, char packetData[TCPServer::maximumPacketLength_bytes], int packetLength_bytes)
{
    // Packet received from the network.

    // Parse the packet header.
    OANetHeader* packetHeader = (OANetHeader*)packetData;

    // Depending on the packet type, process as appropriate.
    switch (packetHeader->packetType)
    {
    case OANetHeader::PacketTypes::EntityState:
        // Entity State Packet

        if (this->isServerActive())
        {
            // We are a TCP Server, relay incoming EntityState packets to all clients.
            tcpServer.sendPacketToClients(sourceConnection, packetData, packetLength_bytes);
        }

        // Process the packet locally.

        // Interpret the incoming data as an entity state structure.
        OANetEntityState* entityStatePacket = (OANetEntityState*)packetData;

        // Get the BodyModel for the incoming packet.
        BodyModel* incomingBody;
        Entity* incomingEntity;
        if (orbitalAero->hasBody(entityStatePacket->index))
        {
            // Existing body found, get its reference from the orbital aero.
            incomingBody = orbitalAero->getBody(entityStatePacket->index);
            incomingEntity = (Entity*)incomingBody->hostData;
        }
        else
        {
            // Existing body not found, create a new body in the orbital aero.
            orbitalAero->createBody(entityStatePacket->index);
            incomingBody = orbitalAero->getBody(entityStatePacket->index);
            
            // Establish an entity for the body and associate a visual model.
            incomingEntity = new Entity(incomingBody);
            incomingBody->hostData = incomingEntity;
            incomingEntity->visualModelObject = &visualModelLibrary->planet;
            // Set the source for the entity as OANet.
            incomingEntity->source = Entity::SourceEnum::OANet;
        }

        incomingEntity->timeOfLastUpdate_seconds = Timer::instance()->getSystemTime_seconds();

        // Update position & orientation from the packet.
        incomingBody->location_meters = entityStatePacket->location_meters;
        incomingBody->linearVelocity_metersPerSecond = entityStatePacket->linearVelocity_metersPerSecond;
        incomingBody->linearAcceleration_metersPerSecond2 = entityStatePacket->linearAcceleration_metersPerSecond2;
        incomingBody->orientation_quaternions = entityStatePacket->orientation_quaternions;

        // Update physical properties from the packet.
        incomingBody->massBase_kilograms = entityStatePacket->massBase_kilograms;
        incomingBody->radius_meters = entityStatePacket->radius_meters;

        break;
    }

    return;
}

// Start a TCP server at a specified port.
bool OANet::startServer(int tcpPort)
{
    return tcpServer.startServer(tcpPort);
}

// Stop the TCP server.
bool OANet::stopServer()
{
    return tcpServer.stopServer();
}

// Return if we're actively hosting a server.
bool OANet::isServerActive()
{
    return tcpServer.isActive();
}

// Return if we're a client connected to a server.
bool OANet::isConnectedToServer()
{
    return tcpClient.isConnected();
}

// Connect as a client to a server at a specified IP address and port.
bool OANet::connectToServer(char ipAddress[16], int port)
{
    return tcpClient.tcpConnect(ipAddress, port);
}

// Disconnect from a server.
bool OANet::disconnectFromServer()
{
    return tcpClient.tcpDisconnect();

}

// Send an entity to the connected networks.
bool OANet::sendEntityState(Entity* entity)
{
    // Create an entity state packet from the entity object.
    OANetEntityState outgoingEntityStatePacket(entity);

    if (tcpServer.isActive())
    {
        // I am acting as a server, send the entity to all the clients.
        tcpServer.sendPacketToClients(nullptr, (char*)&outgoingEntityStatePacket, sizeof(outgoingEntityStatePacket));
    }
    else if (tcpClient.isConnected())
    {
        // I am a client connected to a server, send only to the server.
        tcpClient.sendPacketToTcp((char*)&outgoingEntityStatePacket, sizeof(outgoingEntityStatePacket));
    }

    return true;
}


OANet Packets


The OANet will eventually support many packets. To be able to parse different packet types, I'm creating a common OANetHeader that all packets will contain. The first item of that header is a single byte that contains the PacketType. The next item is the timeStamp the packet was sent.

OANetHeader.h



#pragma once

#include "stdint.h"

// Header for all OANet Network Messages
struct OANetHeader
{
    // Enumerations of the Packet Types that are sent over OANet
    enum class PacketTypes : uint8_t
    {
        EntityState = 1
    };

    // The packet type of the message.
    PacketTypes packetType;

    // The time stamp the packet was sent.
    double timeStamp_seconds;
};


OANetEntityState.h


The first message I'm supporting is an Entity State. It includes the OANetHeader first (as all my network structures will) followed by the entity's unique index, location, velocity, acceleration, orientation, and other important properties.

#pragma once

#include "stdint.h"
#include "OANetHeader.h"
#include "..\..\..\..\OrbitalAeroModel\Vector.h"
#include "..\..\..\..\OrbitalAeroModel\Quaternion.h"

class Entity;

struct OANetEntityState
{
    OANetHeader header;
    uint64_t index; // Unique identification of this body.
    Vector location_meters;
    Vector linearVelocity_metersPerSecond;
    Vector linearAcceleration_metersPerSecond2;
    Quaternion orientation_quaternions;
    double massBase_kilograms;
    double radius_meters;

    OANetEntityState(Entity* entity);
};


OANetEntityState.cpp


The constructor for the OANetEntityState takes in an Entity pointer and will populate the packet as necessary.

#include <vector>
#include "..\..\..\Entity.h"
#include "..\..\..\..\OrbitalAeroModel\Euler.h"
#include "..\..\..\..\OrbitalAeroModel\Vector.h"
#include "..\..\..\..\OrbitalAeroModel\Quaternion.h"
#include "..\..\..\..\OrbitalAeroModel\ControlSystemModel.h"
#include "..\..\..\..\OrbitalAeroModel\PartModel.h"
#include "..\..\..\..\OrbitalAeroModel\BodyModel.h"

#include "OANetHeader.h"
#include "OANetEntityState.h"

OANetEntityState::OANetEntityState(Entity* entity)
{
    this->header.packetType = OANetHeader::PacketTypes::EntityState;
    this->header.timeStamp_seconds = 0.0;
    this->index = entity->bodyObject->index;
    this->location_meters = entity->bodyObject->location_meters;
    this->linearVelocity_metersPerSecond = entity->bodyObject->linearVelocity_metersPerSecond;;
    this->linearAcceleration_metersPerSecond2 = entity->bodyObject->linearAcceleration_metersPerSecond2;
    this->orientation_quaternions = entity->bodyObject->orientation_quaternions;
    this->massBase_kilograms = entity->bodyObject->massBase_kilograms;
    this->radius_meters = entity->bodyObject->radius_meters;
    return;
}


TCP


Now into the meat of the networking code. I'm actually not going to go into too many details here as it's fairly well commented and covered in a previous blog post. The basic idea is I have a TCPServer class and a TCPClient class.

Creating a server in the TCPServer class causes it to spawn a new thread that listens for connections. When it establishes a new connection, it spools off another thread to handle communication with that new connection. The previous thread listening for connections continues in case someone else wants to connect. If another clients comes in, then another new thread is creating to listen for packets from that second client.

TCPServer.h



#pragma once

#define WIN32_LEAN_AND_MEAN
#include <WinSock2.h>
#include <ws2tcpip.h>
#include <mutex>
#include <vector>

class TCPServer
{
public:

    // Constant denoting the maximum packet size that can be handled.
    static const int maximumPacketLength_bytes = 8192;

    // Class that stores information related to each of the established connections.
    class ConnectionModel
    {
    public:
        SOCKET clientSocket;
        // Add other variables as desired associated with this client connection.

        // Constructor defining the client socket.
        ConnectionModel(SOCKET newClientSocket)
        {
            this->clientSocket = newClientSocket;
        }
    };

    // TCP Server Constructor
    TCPServer(void* newHostNetwork, void(*newCallbackProcessIncomingPacket)(void* hostNetwork, TCPServer::ConnectionModel* sourceConnection, char packetData[TCPServer::maximumPacketLength_bytes], int packetLength_bytes));

    // TCP Server Destructor
    ~TCPServer();

    // Start a TCP server at the given port.
    bool startServer(int tcpPort);

    // Stop the TCP server.
    bool stopServer();

    // Send a packet to all clients except to the client that originated the packet (sourceConnection).
    bool sendPacketToClients(ConnectionModel* sourceConnection, char packetData[maximumPacketLength_bytes], int packetLength_bytes);

    // Return if the server is active and listening for connections.
    bool isActive();

private:

    // Socket that establishes new connections.
    SOCKET listenSocket;

    // Flag if the server is active.
    bool _isActive;

    // Reference to the host network that instantiated the TCP client object.
    // Packets will be relayed to the host network.
    void* hostNetwork;

    // Multithreading lock to protect changes to connections list while iterating through it.
    std::mutex clientConnectionsMutex;

    // List that holds all client connections.
    std::vector<ConnectionModel*> clientConnections;

    // Disconnect all clients. (This does not prevent new clients from connecting later.)
    bool disconnectAllClients();

    // Add a new client to the maintained connections.
    ConnectionModel* addClient(SOCKET clientSocket);

    // Remove a client from the maintained connections.
    bool removeClient(ConnectionModel* clientConnection);

    // Thread function listening for new clients.
    void tcpClientListener();

    // Thread function for listening for packets from clients.
    void tcpPacketListener(ConnectionModel* clientConnection);

    // Variable of the function callback in the host network to call when a packet is received.
    void(*callbackProcessIncomingPacket)(void *hostNetwork, ConnectionModel* sourceConnection, char packetData[maximumPacketLength_bytes], int packetLength_bytes);
};


TCPServer.cpp



#include <process.h>
#include <stdlib.h>
#include <stdio.h>
#include <thread>
#include <iostream>

#pragma comment (lib, "Ws2_32.lib")

#include "TCPServer.h"

//#include <windows.h>

// TCP Server Constructor
TCPServer::TCPServer(void* newHostNetwork, void(*newCallbackProcessIncomingPacket)(void* hostNetwork, TCPServer::ConnectionModel* sourceConnection, char packetData[TCPServer::maximumPacketLength_bytes], int packetLength_bytes))
{
    this->hostNetwork = newHostNetwork;
    this->callbackProcessIncomingPacket = newCallbackProcessIncomingPacket;

    this->_isActive = false;

    // Initialize Winsock.
    WSADATA wsd;
    if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
    {
        std::cout << "WSAStartup failed." << std::endl;
    }
    return;
}

// TCP Server Destructor
TCPServer::~TCPServer()
{
    WSACleanup();
    return;
}

// Start a TCP server at the given port.
bool TCPServer::startServer(int tcpPort)
{
    // Bound checking for given TCP port.
    if (tcpPort <= 0 || tcpPort >= 65535)
    {
        std::cout << "Invalid port: " << tcpPort << std::endl;
        return false;
    }

    std::cout << "Listening on port: " << tcpPort << std::endl;

    // Setup address info hints.
    struct addrinfo addressInfoHints;
    memset(&addressInfoHints, 0, sizeof(addressInfoHints));
    addressInfoHints.ai_family = AF_INET;
    addressInfoHints.ai_socktype = SOCK_STREAM;
    addressInfoHints.ai_protocol = IPPROTO_TCP;
    addressInfoHints.ai_flags = AI_PASSIVE;

    // Get address TCP port string.
    char portString[20];
    sprintf(portString, "%d", tcpPort);

    // Resolve the server address and port.
    struct addrinfo* addressInfoResult = NULL;
    int getaddrinfoReturn = getaddrinfo(NULL, portString, &addressInfoHints, &addressInfoResult);
    if (getaddrinfoReturn != 0)
    {
        std::cout << "getaddrinfo failed." << std::endl;
        return false;
    }

    // Create a TCP socket to listen for clients.
    listenSocket = socket(addressInfoResult->ai_family, addressInfoResult->ai_socktype, addressInfoResult->ai_protocol);
    if (listenSocket == INVALID_SOCKET)
    {
        std::cout << "listen socket failed." << std::endl;
        freeaddrinfo(addressInfoResult);
        return false;
    }

    // Bind the TCP socket to the address.
    int bindReturn = bind(listenSocket, addressInfoResult->ai_addr, (int)addressInfoResult->ai_addrlen);
    if (bindReturn == SOCKET_ERROR)
    {
        std::cout << "bind socket failed." << std::endl;
        freeaddrinfo(addressInfoResult);
        closesocket(listenSocket);
        return false;
    }

    freeaddrinfo(addressInfoResult);

    // Initialize / clear the connections list.
    clientConnections.clear();

    // Start the TCP Client Listener thread to listen for connections.
    std::thread{ &TCPServer::tcpClientListener, this }.detach();

    this->_isActive = true;

    return true;
}

// Stop the TCP server.
bool TCPServer::stopServer()
{
    this->_isActive = false;

    // Close the listen socket to stop receiving new connections.
    closesocket(listenSocket);

    // Disconnect all clients.
    disconnectAllClients();

    return true;
}

// Thread function listening for new clients.
void TCPServer::tcpClientListener()
{
    // Listen for new connections until the listenSocket is closed.
    while (1)
    {
        // listen will wait until a connection is requested on the socket. If the socket is closed
        // while waiting, then listen will break from its wait and throw a socket error.
        int listenResult = listen(this->listenSocket, SOMAXCONN);
        if (listenResult != SOCKET_ERROR)
        {
            // Accept a client socket
            SOCKET clientSocket = accept(this->listenSocket, NULL, NULL);
            if (clientSocket != INVALID_SOCKET)
            {
                // Create a connection to the client and begin a listener thread.
                ConnectionModel* clientConnection = addClient(clientSocket);
                std::thread{ &TCPServer::tcpPacketListener, this, clientConnection }.detach();
            }
            else
            {
                std::cout << "TCP accept failed." << std::endl;
                break;
            }
        }
        else
        {
            std::cout << "TCP listen failed." << std::endl;
            break;
        }
    }

    return;
}

// Thread function for listening for packets from clients.
void TCPServer::tcpPacketListener(ConnectionModel* clientConnection)
{
    char receiveBuffer[maximumPacketLength_bytes];

    // Receive until the client connection is closed.
    while (1)
    {
        // recv will wait until a packet is received on the socket. If the socket is closed
        // while waiting, then recv will break from its wait and throw a socket error.
        int packetLength_bytes = recv(clientConnection->clientSocket, receiveBuffer, maximumPacketLength_bytes, 0);
        if (packetLength_bytes > 0)
        {
            // Relay the packet back to the host for processing.
            callbackProcessIncomingPacket(this->hostNetwork, clientConnection, receiveBuffer, packetLength_bytes);
        }
        else if (packetLength_bytes == 0)
        {
            std::cout << "TCP connection closed." << std::endl;
            break;
        }
        else
        {
            std::cout << "TCP socket receive error." << std::endl;
            break;
        }
    };

    // Client connection was closed, remove it from our connection list.
    removeClient(clientConnection);

    return;
}

// Add a new client to the maintained connections.
TCPServer::ConnectionModel* TCPServer::addClient(SOCKET clientSocket)
{
    // Create a new connection for the socket.
    ConnectionModel* clientConnection = new ConnectionModel(clientSocket);

    clientConnectionsMutex.lock();

    // Add the client to the connections list.
    clientConnections.push_back(clientConnection);

    std::cout << "Client added!" << std::endl;
    std::cout << "Active Connections: " << clientConnections.size() << std::endl;

    clientConnectionsMutex.unlock();

    return clientConnection;
}

// Remove a client from the maintained connections.
bool TCPServer::removeClient(ConnectionModel* clientConnection)
{
    clientConnectionsMutex.lock();

    // Shutdown and close the socket.
    shutdown(clientConnection->clientSocket, SD_BOTH);
    closesocket(clientConnection->clientSocket);

    // Remove the client from our connection list.
    auto removedConnection = std::find(clientConnections.begin(), clientConnections.end(), clientConnection);
    if (removedConnection != clientConnections.end())
    {
        clientConnections.erase(removedConnection);
    }

    // Free the connection memory.
    delete clientConnection;

    std::cout << "Client removed!" << std::endl;
    std::cout << "Active Connections: " << clientConnections.size() << std::endl;

    clientConnectionsMutex.unlock();

    return true;
}

// Disconnect all clients. (This does not prevent new clients from connecting later.)
bool TCPServer::disconnectAllClients()
{
    clientConnectionsMutex.lock();

    // Loop through the connections and close each of their connections.
    std::vector<ConnectionModel*>::iterator clientConnectionIterator;
    for (clientConnectionIterator = clientConnections.begin(); clientConnectionIterator != clientConnections.end(); clientConnectionIterator++)
    {
        ConnectionModel* loopClientConnection = *clientConnectionIterator;

        // Closing the socket will cause the listener threads for those sockets to close.
        shutdown(loopClientConnection->clientSocket, SD_BOTH);
        closesocket(loopClientConnection->clientSocket);
    }

    clientConnectionsMutex.unlock();

    return true;
}

// Send a packet to all clients except to the client that originated the packet (sourceConnection).
bool TCPServer::sendPacketToClients(ConnectionModel* sourceConnection, char packetData[maximumPacketLength_bytes], int packetLength_bytes)
{
    clientConnectionsMutex.lock();

    // Loop through the connections and send the packet to each.
    std::vector<ConnectionModel*>::iterator clientConnectionIterator;
    for (clientConnectionIterator = clientConnections.begin(); clientConnectionIterator != clientConnections.end(); clientConnectionIterator++)
    {
        ConnectionModel* loopClientConnection = *clientConnectionIterator;

        // Skip sending the packet if it's to the source.
        if ((sourceConnection != nullptr) && (loopClientConnection->clientSocket == sourceConnection->clientSocket))
        {
            continue;
        }

        // Send the packet on the socket!
        int bytesSent = send(loopClientConnection->clientSocket, packetData, packetLength_bytes, 0);
        if (bytesSent == SOCKET_ERROR)
        {
            std::cout << "Send failed." << std::endl;
        }
    }

    clientConnectionsMutex.unlock();

    return true;
}

// Return if the server is active and listening for connections.
bool TCPServer::isActive()
{
    return this->_isActive;
}



TCPClient.h



#pragma once

#define WIN32_LEAN_AND_MEAN
#include <WinSock2.h>
#include <ws2tcpip.h>

#include "TCPServer.h"

class TCPClient
{
private:
    // Flag if the client has an active connection to a server.
    bool _isConnected;

    // Thread function listening for TCP packets from the server.
    void tcpListener();

    // Socket of the TCP connection to the server.
    SOCKET tcpSocket;

    // Variable of the function callback in the host network to call when a packet is received.
    void(*callbackProcessIncomingPacket)(void *hostNetwork, TCPServer::ConnectionModel* sourceConnection, char packetData[TCPServer::maximumPacketLength_bytes], int packetLength_bytes);

    // Reference to the host network that instantiated the TCP client object.
    // Packets will be relayed to the host network.
    void* hostNetwork;

public:

    // Constant denoting the maximum packet size that can be handled.
    static const int maximumPacketLength_bytes = 8192;

    // Constructor for the TCP Client.
    TCPClient(void* newHostNetwork, void(*newCallbackProcessIncomingPacket)(void* hostNetwork, TCPServer::ConnectionModel* sourceConnection, char packetData[TCPServer::maximumPacketLength_bytes], int packetLength_bytes));

    // Destructor for the TCP Client.
    ~TCPClient();

    // Connect to the TCP server at the specified server address and port.
    bool tcpConnect(char serverAddress[16], int tcpPort);

    // Close the TCP connection with the server.
    bool tcpDisconnect();

    // Send a packet to the TCP server.
    bool sendPacketToTcp(char packetData[maximumPacketLength_bytes], int packetLength_bytes);

    // Return if currently connected to a server.
    bool isConnected();
};


TCPClient.cpp


#include <process.h>
#include <stdio.h>
#include <map>
#include <string>
#include <iostream>
#include <sstream>
#include <time.h>
#include <thread>

#pragma comment (lib, "Ws2_32.lib")

#include "TCPClient.h"

// Constructor for the TCP Client.
TCPClient::TCPClient(void* newHostNetwork, void(*newCallbackProcessIncomingPacket)(void* hostNetwork, TCPServer::ConnectionModel* sourceConnection, char packetData[TCPServer::maximumPacketLength_bytes], int packetLength_bytes))
{
    this->hostNetwork = newHostNetwork;
    this->callbackProcessIncomingPacket = newCallbackProcessIncomingPacket;

    _isConnected = false;

    // Initialize Winsock.
    WSADATA wsd;
    if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
    {
        std::cout << "WSAStartup failed." << std::endl;
    }
    return;
}

// Destructor for the TCP Client.
TCPClient::~TCPClient()
{
    // Disconnect the TCP socket.
    tcpDisconnect();

    WSACleanup();
    return;
}

// Connect to the TCP server at the specified server address and port.
bool TCPClient::tcpConnect(char serverAddress[16], int tcpPort)
{
    // Setup address info hints.
    struct addrinfo addressInfoHints;
    memset(&addressInfoHints, 0, sizeof(addressInfoHints));
    addressInfoHints.ai_family = AF_UNSPEC;
    addressInfoHints.ai_socktype = SOCK_STREAM;
    addressInfoHints.ai_protocol = IPPROTO_TCP;

    // Resolve the server address.
    char portString[20];
    sprintf(portString, "%d", tcpPort);
    struct addrinfo* addressInfoResult = NULL;
    int getaddrinfoReturn = getaddrinfo(serverAddress, portString, &addressInfoHints, &addressInfoResult);
    if (getaddrinfoReturn != 0)
    {
        std::cout << "getaddrinfo failed." << std::endl;
        return false;
    }

    // Loop through potential server addresses to attempt connection.
    for (struct addrinfo *ptr = addressInfoResult; ptr != NULL; ptr = ptr->ai_next)
    {
        // Create the TCP socket.
        tcpSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
        if (tcpSocket == INVALID_SOCKET)
        {
            std::cout << "TCP socket failed." << std::endl;
            return false;
        }

        // Connect to the server.
        int connectReturn = connect(tcpSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
        if (connectReturn == SOCKET_ERROR)
        {
            closesocket(tcpSocket);
            tcpSocket = INVALID_SOCKET;
            continue;
        }
        break;
    }

    freeaddrinfo(addressInfoResult);

    if (tcpSocket == INVALID_SOCKET)
    {
        std::cout << "Unable to connect to server!" << std::endl;
        return false;
    }

    _isConnected = true;

    // Start the TCP Listener thread.
    std::thread{ &TCPClient::tcpListener, this }.detach();

    return true;
}

// Close the TCP connection with the server.
bool TCPClient::tcpDisconnect()
{
    // Shutdown and close the socket.
    shutdown(tcpSocket, SD_BOTH);
    closesocket(tcpSocket);

    return true;
}

// Thread function listening for TCP packets from the server.
void TCPClient::tcpListener()
{
    char receiveBuffer[maximumPacketLength_bytes];

    // Receive until the connection is closed.
    while (1)
    {
        // recv will wait until a packet is received on the socket. If the socket is closed
        // while waiting, then recv will break from its wait and throw a socket error.
        int packetLength_bytes = recv(tcpSocket, receiveBuffer, maximumPacketLength_bytes, 0);
        if (packetLength_bytes > 0)
        {
            // Relay the packet back to the host for processing.
            callbackProcessIncomingPacket(this->hostNetwork, nullptr, receiveBuffer, packetLength_bytes);
        }
        else if (packetLength_bytes == 0)
        {
            std::cout << "TCP connection closed." << std::endl;
            break;
        }
        else
        {
            std::cout << "TCP socket receive error." << std::endl;
            break;
        } // receiveSize_bytes
    }

    _isConnected = false;

    return;
}

// Return if currently connected to a server.
bool TCPClient::isConnected()
{
    return _isConnected;
}

// Send a packet to the TCP server.
bool TCPClient::sendPacketToTcp(char packetData[maximumPacketLength_bytes], int packetLength_bytes)
{
    // Send the packet.
    int sendResult = send(tcpSocket, packetData, packetLength_bytes, 0);
    if (sendResult == packetLength_bytes)
    {
        // Bytes successfully sent.
        return true;
    }
    else if (sendResult < 0)
    {
        //std::cout << "TCP send failed at socket level." << std::endl;
        return false;
    }
    else
    {
        //std::cout << "TCP failed to send entire message." << std::endl;
        return false;
    }
}



Copyright (c) 2017 Clinton Kam
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.