public void NotifyByCharacterID(PyPacket packet, PyAddressBroadcast destination) { lock (this.Clients) { PyList idlist = destination.IDsOfInterest; foreach (PyDataType idData in idlist) { PyInteger id = idData as PyInteger; foreach (KeyValuePair <long, ClientConnection> entry in this.Clients) { if (entry.Value.CharacterID == id) { // use the key instead of AccountID as this should be faster packet.UserID = entry.Key; // change the ids of interest to hide the character's we've notified destination.IDsOfInterest = new PyDataType[] { id }; // queue the packet for the user entry.Value.Socket.Send(packet); } } } } }
private void ReceivePacketCallback(PyDataType input) { Log.Debug("Processing packet from node"); PyPacket packet = input; if (packet.Type == PyPacket.PacketType.CALL_RSP || packet.Type == PyPacket.PacketType.ERRORRESPONSE) { if (packet.Destination is PyAddressClient) { Log.Trace($"Sending packet to client {packet.UserID}"); this.ConnectionManager.NotifyClient((int)(packet.UserID), packet); } else if (packet.Destination is PyAddressNode address) { Log.Trace($"Sending packet to node {address.NodeID}"); this.ConnectionManager.NotifyNode((int)(address.NodeID), packet); } else if (packet.Destination is PyAddressBroadcast) { Log.Error("Broadcast packets not supported yet"); } // TODO: Handle Broadcast packets } else { Log.Error($"Wrong packet name: {packet.TypeString}"); } }
public void SendProvisionalResponse(CallInformation answerTo, ProvisionalResponse response) { PyPacket result = new PyPacket(PyPacket.PacketType.CALL_RSP); // ensure destination has clientID in it PyAddressClient source = answerTo.From as PyAddressClient; source.ClientID = answerTo.Client.AccountID; // switch source and dest result.Source = answerTo.To; result.Destination = source; result.UserID = source.ClientID; result.Payload = new PyTuple(0); result.OutOfBounds = new PyDictionary { ["provisional"] = new PyTuple(3) { [0] = response.Timeout, // macho timeout in seconds [1] = response.EventID, [2] = response.Arguments } }; this.Socket.Send(result); }
private void ReceiveNormalPacketCallback(PyDataType ar) { PyPacket packet = ar; this.ClientManager.TryGetClient(packet.UserID, out Client client); switch (packet.Type) { case PyPacket.PacketType.CALL_REQ: this.HandleCallReq(packet, client); break; case PyPacket.PacketType.SESSIONCHANGENOTIFICATION: this.HandleSessionChangeNotification(packet, client); break; case PyPacket.PacketType.PING_REQ: this.HandlePingReq(packet, client); break; case PyPacket.PacketType.CALL_RSP: this.HandleCallRes(packet, client); break; case PyPacket.PacketType.NOTIFICATION: this.HandleNotification(packet, client); break; } // send any notification that might be pending client?.SendPendingNotifications(); }
private void HandleNotification(PyPacket packet) { if (packet.Destination is PyAddressBroadcast == false) { Log.Error("Received a notification that is not a broadcast..."); return; } PyAddressBroadcast destination = packet.Destination as PyAddressBroadcast; if (packet.UserID != -1) { Log.Trace($"Relaying notification to client {packet.UserID}"); this.ConnectionManager.NotifyClient((int)packet.UserID, packet); return; } // special situation, the ClusterController has to take care of fullfiling the proper information // in the packet, like UserID switch (destination.IDType) { case "solarsystemid2": this.ConnectionManager.NotifyBySolarSystemID2(packet, destination); break; case "constellationid": this.ConnectionManager.NotifyByConstellationID(packet, destination); break; case "corpid": this.ConnectionManager.NotifyByCorporationID(packet, destination); break; case "regionid": this.ConnectionManager.NotifyByRegionID(packet, destination); break; case "charid": this.ConnectionManager.NotifyByCharacterID(packet, destination); break; case "stationid": this.ConnectionManager.NotifyByStationID(packet, destination); break; case "allianceid": this.ConnectionManager.NotifyByAllianceID(packet, destination); break; case "nodeid": this.ConnectionManager.NotifyByNodeID(packet, destination); break; // TODO: IMPLEMENT OTHER NOTIFICATION ID TYPES BASED ON THE CLIENT CODE IF THEY'RE ACTUALLY USEFUL default: Log.Error($"Unexpected broadcast with idtype {destination.IDType.Value} and negative userID (autofill by ClusterController)"); break; } }
/// <summary> /// Creates a session change notification including only the last changes to the session /// </summary> /// <param name="container">Information about the current node the client is in</param> /// <returns>The packet ready to be sent</returns> private PyPacket CreateEmptySessionChange(NodeContainer container) { // Fill all the packet data, except the dest/source SessionChangeNotification scn = new SessionChangeNotification { Changes = Session.GenerateSessionChange() }; if (scn.Changes.Length == 0) { // Nothing to do return(null); } // add ourselves as nodes of interest // TODO: DETECT NODE OF INTEREST BASED ON THE SERVER THAT HAS THE SOLAR SYSTEM LOADED IN scn.NodesOfInterest.Add(container.NodeID); PyPacket packet = new PyPacket(PyPacket.PacketType.SESSIONCHANGENOTIFICATION) { Source = new PyAddressNode(container.NodeID), Destination = new PyAddressClient(this.Session["userid"] as PyInteger), UserID = this.Session["userid"] as PyInteger, Payload = scn, OutOfBounds = new PyDictionary() { { "channel", "sessionchange" } } }; return(packet); }
public PyPacket CreateEmptySessionChange() { // Fill all the packet data, except the dest/source SessionChangeNotification scn = new SessionChangeNotification { Changes = Session.GenerateSessionChange() }; if (scn.Changes.Length == 0) { // Nothing to do return(null); } PyPacket packet = new PyPacket(PyPacket.PacketType.SESSIONCHANGENOTIFICATION) { UserID = this.Session["userid"] as PyInteger, Payload = scn, OutOfBounds = new PyDictionary { ["channel"] = "sessionchange" } }; return(packet); }
private void ReceivePacketCallback(PyDataType input) { Log.Debug("Processing packet from node"); PyPacket packet = input; // alter the ping responses from nodes to add the extra required information if (packet.Type == PyPacket.PacketType.PING_RSP) { HandlePingRsp(packet); } switch (packet.Type) { // relay packet if needed case PyPacket.PacketType.CALL_RSP: case PyPacket.PacketType.CALL_REQ: case PyPacket.PacketType.ERRORRESPONSE: case PyPacket.PacketType.PING_RSP: this.RelayPacket(packet); break; case PyPacket.PacketType.SESSIONCHANGENOTIFICATION: this.HandleSessionChangeNotification(packet); break; case PyPacket.PacketType.NOTIFICATION: this.HandleNotification(packet); break; default: Log.Error($"Wrong packet name: {packet.TypeString}"); break; } }
public PyDataType SetSessionChangeDestination(PyPacket packet) { packet.Source = new PyAddressNode(Network.PROXY_NODE_ID, 0); packet.Destination = new PyAddressClient(this.Session["userid"] as PyInteger, 0); return(packet); }
public PyPacket CreateEmptySessionChange() { // Fill all the packet data, except the dest/source SessionChangeNotification scn = new SessionChangeNotification(); scn.changes = Session.EncodeChanges(); if (scn.changes.Dictionary.Count == 0) { // Nothing to do return(null); } Dictionary <int, Connection> nodes = ConnectionManager.Nodes; // Add all the nodeIDs foreach (KeyValuePair <int, Connection> node in nodes) { scn.nodesOfInterest.Items.Add(new PyInt(node.Key)); } PyPacket p = new PyPacket(); p.type_string = "macho.SessionChangeNotification"; p.type = Macho.MachoNetMsg_Type.SESSIONCHANGENOTIFICATION; p.userID = (uint)AccountID; p.payload = scn.Encode().As <PyTuple>(); p.named_payload = new PyDict(); p.named_payload.Set("channel", new PyString("sessionchange")); return(p); }
public PyDataType SetSessionChangeDestination(PyPacket packet, int node) { packet.Source = new PyAddressNode(1, 0); packet.Destination = new PyAddressNode(node, 0); return(packet); }
public void SendException(CallInformation answerTo, PyDataType content) { // build a new packet with the correct information PyPacket result = new PyPacket(PyPacket.PacketType.ERRORRESPONSE); // ensure destination has clientID in it PyAddressClient source = answerTo.From as PyAddressClient; source.ClientID = answerTo.Client.AccountID; // switch source and dest result.Source = answerTo.To; result.Destination = source; result.UserID = source.ClientID; result.Payload = new PyTuple(3) { [0] = (int)answerTo.PacketType, [1] = (int)MachoErrorType.WrappedException, [2] = new PyTuple(1) { [0] = new PySubStream(content) }, }; this.ClusterConnection.Send(result); }
private void HandlePingReq(PyPacket packet, Client client) { // alter package to include the times the data PyTuple handleMessage = new PyTuple(3); PyAddressClient source = packet.Source as PyAddressClient; // this time should come from the stream packetizer or the socket itself // but there's no way we're adding time tracking for all the goddamned packets // so this should be sufficient handleMessage[0] = DateTime.UtcNow.ToFileTime(); handleMessage[1] = DateTime.UtcNow.ToFileTime(); handleMessage[2] = "server::handle_message"; PyTuple turnaround = new PyTuple(3); turnaround[0] = DateTime.UtcNow.ToFileTime(); turnaround[1] = DateTime.UtcNow.ToFileTime(); turnaround[2] = "server::turnaround"; (packet.Payload[0] as PyList)?.Add(handleMessage); (packet.Payload[0] as PyList)?.Add(turnaround); // change to a response packet.Type = PyPacket.PacketType.PING_RSP; // switch source and destination packet.Source = packet.Destination; packet.Destination = source; // queue the packet back this.Socket.Send(packet); }
private PyObject OldHandle(PyObject packet) { // Check for login packets and just forward them if (packet is PyTuple) { // Those are the first packets, sent by both client and server return(packet); } else if (packet is PyInt) { // This is only sent by the server return(packet); } else if (packet is PyString) { // This is only sent by the server return(packet); } else if (packet is PyDict) { // Packet sent by the client(HandshakeAck) // We need to modify it in order to put our own client address, as it isnt the same as the address that the server sends PyDict handshake = packet as PyDict; PyDict session = handshake.Get("session_init") as PyDict; session.Set("address", new PyString(socket.GetAddress())); handshake.Set("session_init", session); return(handshake); } else if (packet is PyObjectEx) // Exceptions... just check the type and decide what to do { PyException exception = new PyException(); if (exception.Decode(packet) == true) // Ignore the error { Log.Debug("Exceptions", "Got exception of type " + exception.exception_type); } return(packet); } else // Normal packets { PyPacket p = new PyPacket(); if (p.Decode(packet) == false) { // Big problem here, we dont know who to send this Log.Error("Client", "Cannot decode PyPacket"); } else { return(HandlePyPacket(p)); } return(packet); } }
/// <summary> /// Prepares and sends a session change notification to this client /// </summary> public void SendSessionChange() { // build the session change PyPacket sessionChangeNotification = this.CreateEmptySessionChange(this.Container); // and finally send the client the required data this.ClusterConnection.Send(sessionChangeNotification); }
public void NotifyAllNodes(PyPacket packet) { lock (this.Nodes) { foreach ((long _, NodeConnection connection) in this.Nodes) { connection.Socket.Send(packet); } } }
public void NotifyAllNodes(PyPacket packet) { lock (this.Nodes) { foreach (KeyValuePair <long, NodeConnection> entry in this.Nodes) { entry.Value.Socket.Send(packet); } } }
private void HandleNotification(PyPacket packet, Client client) { if (packet.Source is PyAddressAny) { this.HandleBroadcastNotification(packet); return; } PyTuple callInfo = ((packet.Payload[0] as PyTuple)[1] as PySubStream).Stream as PyTuple; PyList objectIDs = callInfo[0] as PyList; string call = callInfo[1] as PyString; if (call != "ClientHasReleasedTheseObjects") { Log.Error($"Received notification from client with unknown method {call}"); return; } // search for the given objects in the bound service // and sure they're freed foreach (PyTuple objectID in objectIDs.GetEnumerable <PyTuple>()) { if (objectID[0] is PyString == false) { Log.Fatal("Expected bound call with bound string, but got something different"); return; } string boundString = objectID[0] as PyString; // parse the bound string to get back proper node and bound ids Match regexMatch = Regex.Match(boundString, "N=([0-9]+):([0-9]+)"); if (regexMatch.Groups.Count != 3) { Log.Fatal($"Cannot find nodeID and boundID in the boundString {boundString}"); return; } int nodeID = int.Parse(regexMatch.Groups[1].Value); int boundID = int.Parse(regexMatch.Groups[2].Value); if (nodeID != this.Container.NodeID) { Log.Fatal("Got a ClientHasReleasedTheseObjects call for an object ID that doesn't belong to us"); // TODO: MIGHT BE A GOOD IDEA TO RELAY THIS CALL TO THE CORRECT NODE // TODO: INSIDE THE NETWORK, AT LEAST THAT'S WHAT CCP IS DOING BASED // TODO: ON THE CLIENT'S CODE... NEEDS MORE INVESTIGATION return; } this.BoundServiceManager.FreeBoundService(boundID); } }
private void HandleSessionChangeNotification(PyPacket packet, Client client) { Log.Debug($"Updating session for client {packet.UserID}"); // ensure the client is registered in the node and store his session if (this.ClientManager.Contains(packet.UserID) == false) { this.ClientManager.Add(packet.UserID, this.DependencyInjector.GetInstance <Client>()); } this.ClientManager.Get(packet.UserID).UpdateSession(packet); }
public PyObject SetSessionChangeDestination(PyPacket p) { p.source.type = PyAddress.AddrType.Node; p.source.typeID = (ulong)nodeID; p.source.callID = 0; p.dest.type = PyAddress.AddrType.Client; p.dest.typeID = (ulong)ClientManager.GetClientID(this); p.dest.callID = 0; return(p.Encode()); }
public PyObject SetSessionChangeDestination(PyPacket p, int node) { // The session change info should never come from the client p.source.type = PyAddress.AddrType.Node; p.source.typeID = (ulong)1; p.source.callID = 0; p.dest.type = PyAddress.AddrType.Node; p.dest.typeID = (ulong)node; p.dest.callID = 0; return(p.Encode()); }
public void UpdateSession(PyPacket packet) { if (packet.Payload.TryGetValue(0, out PyTuple sessionData) == false) { throw new InvalidDataException("SessionChangeNotification expected a payload of size 1"); } if (sessionData.TryGetValue(1, out PyDictionary differences) == false) { throw new InvalidDataException("SessionChangeNotification expected a differences collection"); } this.Session.LoadChanges(differences); }
public void SendSessionChange() { PyPacket sc = CreateEmptySessionChange(); PyObject client = SetSessionChangeDestination(sc); PyObject node = SetSessionChangeDestination(sc, NodeID); if (sc != null) { Send(client); ConnectionManager.NotifyConnection(NodeID, node); } }
// TODO: MOVE THIS CODE TO THE SESSION HANDLER INSTEAD public void SendSessionChange() { PyPacket packet = CreateEmptySessionChange(); if (packet is null) { return; } PyDataType client = SetSessionChangeDestination(packet); this.Socket.Send(client); this.ConnectionManager.NotifyAllNodes(packet); }
public void UpdateSession(PyPacket from) { Log.Debug("Client", "Updating session for client"); // We should add a Decode method to SessionChangeNotification... PyTuple payload = from.payload; PyDict changes = payload[0].As <PyTuple>()[1].As <PyTuple>()[0].As <PyDict>(); // Update our local session foreach (PyString key in changes.Dictionary.Keys) { session.Set(key.Value, changes[key.Value].As <PyTuple>()[1]); } }
public void NotifyNode(long nodeID, string type, PyTuple data) { PyPacket notification = new PyPacket(PyPacket.PacketType.NOTIFICATION); notification.Source = new PyAddressAny(0); notification.Destination = new PyAddressNode(nodeID); notification.Payload = new PyTuple(2) { [0] = type, [1] = data }; notification.OutOfBounds = new PyDictionary(); notification.UserID = 0; lock (this.Nodes) this.Nodes[nodeID].Socket.Send(notification); }
public void SendCallResult(CallInformation answerTo, PyDataType content) { PyPacket result = new PyPacket(PyPacket.PacketType.CALL_RSP); // ensure destination has clientID in it PyAddressClient source = answerTo.From as PyAddressClient; source.ClientID = answerTo.Client.AccountID; // switch source and dest result.Source = answerTo.To; result.Destination = source; result.UserID = source.ClientID; result.Payload = new PyTuple(new PyDataType[] { new PySubStream(content) }); this.Socket.Send(result); }
private void HandleSessionChangeNotification(PyPacket packet, Client client) { Log.Debug($"Updating session for client {packet.UserID}"); // ensure the client is registered in the node and store his session if (client == null) { this.ClientManager.Add( packet.UserID, client = new Client( this.Container, this.ClusterConnection, this.ServiceManager, this.TimerManager, this.ItemFactory, this.CharacterManager, this.SystemManager, this.NotificationManager, this ) ); } client.UpdateSession(packet); }
public PyObject CreateSessionChange() { PyPacket p = CreateEmptySessionChange(); if (p == null) { return(null); } p.source.type = PyAddress.AddrType.Node; p.source.typeID = (ulong)nodeID; p.source.callID = 0; p.dest.type = PyAddress.AddrType.Client; p.dest.typeID = (ulong)GetAccountID(); p.dest.callID = 0; return(p.Encode()); }
private void RelayPacket(PyPacket packet) { switch (packet.Destination) { case PyAddressClient _: Log.Trace($"Sending packet to client {packet.UserID}"); this.ConnectionManager.NotifyClient((int)(packet.UserID), packet); break; case PyAddressNode address: Log.Trace($"Sending packet to node {address.NodeID}"); this.ConnectionManager.NotifyNode((int)(address.NodeID), packet); break; case PyAddressBroadcast _: Log.Error("Broadcast packets not supported yet"); break; } }