public PyDataType DestroyChannel(PyInteger channelID, CallInformation call) { int callerCharacterID = call.Client.EnsureCharacterIsSelected(); // ensure the character has enough permissions if (this.DB.IsCharacterAdminOfChannel(channelID, callerCharacterID) == false) { throw new LSCCannotDestroy("Insufficient permissions"); } // get users in the channel that are online now PyList characters = this.DB.GetOnlineCharsOnChannel(channelID); // remove channel off the database this.DB.DestroyChannel(channelID); // notify everyone in the channel only when it should PyDataType notification = GenerateLSCNotification("DestroyChannel", channelID, new PyTuple(0), call.Client); // notify all characters in the channel call.Client.ClusterConnection.SendNotification("OnLSC", "charid", characters, notification); return(null); }
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); }
public PyDataType LeaveChannels(PyList channels, PyDataType boolUnsubscribe, PyInteger role, CallInformation call) { foreach (PyDataType channelInfo in channels) { if (channelInfo is PyTuple == false) { Log.Error("LSC received a channel identifier in LeaveChannels that doesn't resemble anything we know"); continue; } PyTuple tuple = channelInfo as PyTuple; if (tuple.Count != 2) { Log.Error( "LSC received a tuple for channel in LeaveChannels that doesn't resemble anything we know"); return(null); } PyDataType channelID = tuple[0]; PyBool announce = tuple[1] as PyBool; this.LeaveChannel(channelID, announce, call); } return(null); }
private void ReceiveLowLevelVersionExchangeCallback(PyDataType ar) { try { LowLevelVersionExchange exchange = ar; // reply with the node LowLevelVersionExchange LowLevelVersionExchange reply = new LowLevelVersionExchange { Codename = Game.CODENAME, Birthday = Game.BIRTHDAY, Build = Game.BUILD, MachoVersion = Game.MACHO_VERSION, Version = Game.VERSION, Region = Game.REGION, IsNode = true, NodeIdentifier = "Node" }; this.ClusterConnection.Send(reply); // set the new handler this.ClusterConnection.SetReceiveCallback(ReceiveNodeInitialState); } catch (Exception e) { Log.Error($"Exception caught on LowLevelVersionExchange: {e.Message}"); throw; } }
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(); }
protected LowLevelVersionExchange CheckLowLevelVersionExchange(PyDataType exchange) { LowLevelVersionExchange data = exchange; if (data.birthday != Common.Constants.Game.birthday) { throw new Exception("Wrong birthday in LowLevelVersionExchange"); } if (data.build != Common.Constants.Game.build) { throw new Exception("Wrong build in LowLevelVersionExchange"); } if (data.codename != Common.Constants.Game.codename + "@" + Common.Constants.Game.region) { throw new Exception("Wrong codename in LowLevelVersionExchange"); } if (data.machoVersion != Common.Constants.Game.machoVersion) { throw new Exception("Wrong machoVersion in LowLevelVersionExchange"); } if (data.version != Common.Constants.Game.version) { throw new Exception("Wrong version in LowLevelVersionExchange"); } if (data.isNode == true) { if (data.nodeIdentifier != "Node") { throw new Exception("Wrong node string in LowLevelVersionExchange"); } } return(data); }
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}"); } }
/// <summary> /// <seealso cref="Marshal.ProcessDictionary"/> /// /// Opcodes supported: /// <seealso cref="Opcode.Dictionary"/> /// </summary> /// <param name="opcode">Type of object to parse</param> /// <returns>The decoded python type</returns> /// <exception cref="InvalidDataException">If any error was found in the data</exception> private PyDataType ProcessDictionary(Opcode opcode) { if (opcode != Opcode.Dictionary) { throw new InvalidDataException($"Trying to parse a {opcode} as Dictionary"); } PyDictionary dictionary = new PyDictionary(); uint size = this.mReader.ReadSizeEx(); while (size-- > 0) { PyDataType value = this.Process(false); PyDataType key = this.Process(false); if (key is PyString == false) { throw new InvalidDataException($"Expected String as Dictionary key, but gor {key.GetType()}"); } dictionary[key as PyString] = value; } return(dictionary); }
public PyDataType AccessControl(PyInteger channelID, PyInteger characterID, PyInteger accessLevel, CallInformation call) { int callerCharacterID = call.Client.EnsureCharacterIsSelected(); if (this.DB.IsCharacterOperatorOrAdminOfChannel(channelID, callerCharacterID) == false) { throw new LSCCannotAccessControl("Insufficient permissions"); } this.DB.UpdatePermissionsForCharacterOnChannel(channelID, characterID, accessLevel); PyTuple args = new PyTuple(6) { [0] = characterID, [1] = accessLevel, [2] = new PyNone(), [3] = accessLevel, [4] = "", [5] = accessLevel == ChatDB.CHATROLE_CREATOR }; PyDataType notification = GenerateLSCNotification("AccessControl", channelID, args, call.Client); // get users in the channel that are online now PyList characters = this.DB.GetOnlineCharsOnChannel(channelID); call.Client.ClusterConnection.SendNotification("OnLSC", "charid", characters, notification); // TODO: CHECK IF THIS IS A CHARACTER'S ADDRESS BOOK AND CHECK FOR OTHER CHARACTER'S ADDRESSBOOK STATUS // TODO: TO ENSURE THEY DON'T HAVE US BLOCKED return(null); }
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; } }
private void ParseChannelIdentifier(PyDataType channel, out int channelID, out string channelType, out int?entityID) { switch (channel) { case PyInteger integer: channelID = integer; // positive channel ids are entity ids, negatives are custom user channels entityID = null; if (channelID > ChatDB.MIN_CHANNEL_ENTITY_ID && channelID < ChatDB.MAX_CHANNEL_ENTITY_ID) { entityID = channelID; } // get the full channel identifier channelType = ChatDB.CHANNEL_TYPE_NORMAL; break; case PyTuple tuple: this.ParseTupleChannelIdentifier(tuple, out channelID, out channelType, out entityID); break; default: throw new InvalidDataException("LSC received a wrongly formatted channel identifier"); } // ensure the channelID is the correct one and not an entityID if (entityID is not null) { channelID = this.DB.GetChannelIDFromRelatedEntity((int)entityID, channelID == entityID); } if (channelID == 0) { throw new InvalidDataException("LSC could not determine chatID for the requested chats"); } }
public PyDataType GetMapConnections(PyInteger itemID, PyDataType isRegion, PyDataType isConstellation, PyDataType isSolarSystem, PyDataType isCelestial, PyInteger unknown2 = null, CallInformation call = null) { bool isRegionBool = false; bool isConstellationBool = false; bool isSolarSystemBool = false; bool isCelestialBool = false; if (isRegion is PyBool regionBool) { isRegionBool = regionBool; } if (isRegion is PyInteger regionInt) { isRegionBool = regionInt.Value == 1; } if (isConstellation is PyBool constellationBool) { isConstellationBool = constellationBool; } if (isConstellation is PyInteger constellationInt) { isConstellationBool = constellationInt.Value == 1; } if (isSolarSystem is PyBool solarSystemBool) { isSolarSystemBool = solarSystemBool; } if (isSolarSystem is PyInteger solarSystemInt) { isSolarSystemBool = solarSystemInt.Value == 1; } if (isCelestial is PyBool celestialBool) { isCelestialBool = celestialBool; } if (isCelestial is PyInteger celestialInt) { isCelestialBool = celestialInt.Value == 1; } if (isRegionBool == true) { return(this.DB.GetMapRegionConnection(itemID)); } if (isConstellationBool == true) { return(this.DB.GetMapConstellationConnection(itemID)); } if (isSolarSystemBool == true) { return(this.DB.GetMapSolarSystemConnection(itemID)); } if (isCelestialBool == true) { Log.Error("GetMapConnections called with celestial id. Not implemented yet!"); } return(null); }
public PyDataType CharGetNewTransactions(PyInteger sellBuy, PyInteger typeID, PyDataType clientID, PyInteger quantity, PyDataType fromDate, PyDataType maxPrice, PyInteger minPrice, CallInformation call) { int callerCharacterID = call.Client.EnsureCharacterIsSelected(); TransactionType transactionType = TransactionType.Either; if (sellBuy is PyInteger) { switch ((int)(sellBuy as PyInteger)) { case 0: transactionType = TransactionType.Sell; break; case 1: transactionType = TransactionType.Buy; break; } } return(this.DB.CharGetNewTransactions( callerCharacterID, null, transactionType, typeID, quantity, minPrice )); }
public void Deconstruct(out PyDataType key, out PyDataType value) { this.mPair.Deconstruct(out PyDataType first, out PyDataType second); key = first; value = second; }
/// <summary> /// <seealso cref="Marshal.ProcessSubStream"/> /// /// Opcodes supported: /// <seealso cref="Opcode.SubStream"/> /// </summary> /// <param name="opcode">Type of object to parse</param> /// <returns>The decoded python type</returns> /// <exception cref="InvalidDataException">If any error was found in the data</exception> protected virtual PyDataType ProcessSubStream(Opcode opcode) { if (opcode != Opcode.SubStream) { throw new InvalidDataException($"Trying to parse a {opcode} as SubStream"); } uint length = this.mReader.ReadSizeEx(); PyDataType result = null; // on compressed streams it's impossible to do boundary-checks, so ensure that is taken into account if (this.mReader.BaseStream is ZInputStream) { byte[] buffer = new byte[length]; this.mStream.Read(buffer, 0, buffer.Length); result = ReadFromByteArray(buffer); } else { long start = this.mStream.Position; // this substream has it's own savelist etc, the best way is to create a new unmarshaler using the same // stream so the data can be properly read without much issue result = ReadFromStream(this.mStream); if ((start + length) != this.mStream.Position) { throw new InvalidDataException($"Read past the boundaries of the PySubStream"); } } return(new PySubStream(result)); }
/// <summary> /// <seealso cref="Marshal.ProcessObject"/> /// /// Opcodes supported: /// <seealso cref="Opcode.ObjectType1"/> /// <seealso cref="Opcode.ObjectType2"/> /// </summary> /// <param name="opcode">Type of object to parse</param> /// <returns>The decoded python type</returns> /// <exception cref="InvalidDataException">If any error was found in the data</exception> protected virtual PyDataType ProcessObject(Opcode opcode) { if (opcode != Opcode.ObjectType1 && opcode != Opcode.ObjectType2) { throw new InvalidDataException($"Trying to parse a {opcode} as ObjectEx"); } PyTuple header = this.Process(false) as PyTuple; PyList list = new PyList(); PyDictionary dict = new PyDictionary(); while (this.mReader.PeekChar() != Marshal.PACKED_TERMINATOR) { list.Add(this.Process(false)); } // ignore packed terminator this.mReader.ReadByte(); while (this.mReader.PeekChar() != Marshal.PACKED_TERMINATOR) { PyString key = this.Process(false) as PyString; PyDataType value = this.Process(false); dict[key] = value; } // ignore packed terminator this.mReader.ReadByte(); return(new PyObject(opcode == Opcode.ObjectType2, header, list, dict)); }
public static CachedMethodCallResult FromCacheHint(PyDataType cacheInfo) { return(new CachedMethodCallResult { CacheHint = cacheInfo }); }
public PyException(string type, string reason, PyDataType extra, PyDictionary keywords) { this.Type = type; this.Reason = reason; this.Extra = extra; this.Keywords = keywords; }
private void ReceiveLoginResultResponse(PyDataType packet) { PyTuple data = packet as PyTuple; if (data.Count != 3) { throw new Exception($"Expected tuple to have 3 items but got {data.Count}"); } // Handshake sent when we are mostly in HandshakeAck ack = new HandshakeAck { LiveUpdates = this.GeneralDB.FetchLiveUpdates(), JIT = this.Session["languageID"] as PyString, UserID = this.Session["userid"] as PyInteger, MaxSessionTime = null, UserType = AccountType.USER, Role = this.Session["role"] as PyInteger, Address = this.Session["address"] as PyString, InDetention = null, ClientHashes = new PyList(), UserClientID = this.Session["userid"] as PyInteger }; // send the response first this.Socket.Send(ack); // send the session change this.SendSessionChange(); // finally assign the correct packet handler this.Socket.SetReceiveCallback(ReceivePacketResponse); }
protected override BoundService CreateBoundInstance(PyDataType objectData, CallInformation call) { if (objectData is PyInteger == false) { throw new CustomError("Cannot bind reprocessingSvc service to unknown object"); } PyInteger stationID = objectData as PyInteger; if (this.MachoResolveObject(stationID, 0, call) != this.BoundServiceManager.Container.NodeID) { throw new CustomError("Trying to bind an object that does not belong to us!"); } Station station = this.ItemFactory.GetStaticStation(stationID); // check if the station has the required services if (station.HasService(StaticData.Inventory.Station.Service.ReprocessingPlant) == false) { throw new CustomError("This station does not allow for reprocessing plant services"); } // ensure the player is in this station if (station.ID != call.Client.StationID) { throw new CanOnlyDoInStations(); } Corporation corporation = this.ItemFactory.GetItem <Corporation>(station.OwnerID); ItemInventory inventory = this.ItemFactory.MetaInventoryManager.RegisterMetaInventoryForOwnerID(station, call.Client.EnsureCharacterIsSelected()); return(new reprocessingSvc(this.ReprocessingDB, this.StandingDB, corporation, station, inventory, this.ItemFactory, this.BoundServiceManager, call.Client)); }
private void ReceiveCommandCallback(PyDataType packet) { ClientCommand command = packet; if (command.Command == "QC") { Log.Debug("Received QueueCheck command"); // send player position on the queue this.Socket.Send(new PyInteger(this.ConnectionManager.LoginQueue.Count())); // send low level version exchange required this.SendLowLevelVersionExchange(); // wait for a new low level version exchange again this.Socket.SetReceiveCallback(ReceiveLowLevelVersionExchangeCallback); } else if (command.Command == "VK") { Log.Debug("Received VipKey command"); // next is the placebo challenge this.Socket.SetReceiveCallback(ReceiveCryptoRequestCallback); } else { throw new Exception("Received unknown data!"); } }
private void ReceiveAuthenticationRequestCallback(PyDataType packet) { AuthenticationReq request = packet; if (request.user_password is null) { Log.Trace("Rejected by server; requesting plain password"); // request the user a plain password this.Socket.Send(new PyInteger(1)); // 1 => plain, 2 => hashed return; } // TODO: DINAMICALLY FETCH THIS SO WE SUPPORT TRANSLATIONS if (request.user_languageid != "EN" && request.user_languageid != "RU" && request.user_languageid != "DE") { // default to english language this.Session["languageID"] = "EN"; } else { // set languageid in the session to the one requested as we have translations for that one this.Session["languageID"] = request.user_languageid; } // add the user to the authentication queue this.ConnectionManager.LoginQueue.Enqueue(this, request); }
private void ReceiveLowLevelVersionExchangeCallback(PyDataType ar) { try { LowLevelVersionExchange exchange = CheckLowLevelVersionExchange(ar); // reply with the node LowLevelVersionExchange LowLevelVersionExchange reply = new LowLevelVersionExchange(); reply.codename = Common.Constants.Game.codename; reply.birthday = Common.Constants.Game.birthday; reply.build = Common.Constants.Game.build; reply.machoVersion = Common.Constants.Game.machoVersion; reply.version = Common.Constants.Game.version; reply.region = Common.Constants.Game.region; reply.isNode = true; reply.nodeIdentifier = "Node"; this.Socket.Send(reply); // set the new handler this.Socket.SetReceiveCallback(ReceiveNodeInitialState); } catch (Exception e) { Log.Error($"Exception caught on LowLevelVersionExchange: {e.Message}"); throw; } }
private void ReceiveNodeInitialState(PyDataType ar) { if (ar is PyObjectData == false) { throw new Exception($"Expected PyObjectData for machoNet.nodeInfo but got {ar.GetType()}"); } PyObjectData info = ar as PyObjectData; if (info.Name != "machoNet.nodeInfo") { throw new Exception($"Expected PyObjectData of type machoNet.nodeInfo but got {info.Name}"); } // Update our local info NodeInfo nodeinfo = info; this.Container.NodeID = nodeinfo.nodeID; Log.Debug("Found machoNet.nodeInfo, our new node id is " + nodeinfo.nodeID.ToString("X4")); // load the specified solar systems this.SystemManager.LoadSolarSystems(nodeinfo.solarSystems); // finally set the new packet handler this.Socket.SetReceiveCallback(ReceiveNormalPacketCallback); }
private void FlushReceivingQueue() { if (this.mReceivingSemaphore.WaitOne(0) == true) { // handle required packets while (this.mPacketizer.PacketCount > 0 && this.mPacketReceiveCallback != null) { try { // unmarshal the packet PyDataType packet = Unmarshal.ReadFromByteArray(this.mPacketizer.PopItem()); #if DEBUG this.mPacketLog.Trace(PrettyPrinter.FromDataType(packet)); #endif // and invoke the callback for the packet handling if it is present this.mPacketReceiveCallback.Invoke(packet); } catch (Exception e) { this.HandleException(e); } } // finally free the receiving semaphore this.mReceivingSemaphore.Release(); } // semaphore not acquired, there's something already sending data, so we're sure the data will get there eventually }
private void ReceiveLoginResultResponse(PyDataType packet) { PyTuple data = packet as PyTuple; if (data.Count != 3) { throw new Exception($"Expected tuple to have 3 items but got {data.Count}"); } // Handshake sent when we are mostly in HandshakeAck ack = new HandshakeAck(); ack.live_updates = this.GeneralDB.FetchLiveUpdates(); ack.jit = this.Session["languageID"] as PyString; ack.userid = this.Session["userid"] as PyInteger; ack.maxSessionTime = new PyNone(); ack.userType = Common.Constants.AccountType.User; ack.role = this.Session["role"] as PyInteger; ack.address = this.Session["address"] as PyString; ack.inDetention = new PyNone(); ack.client_hashes = new PyList(); ack.user_clientid = this.Session["userid"] as PyInteger; // send the response first this.Socket.Send(ack); // send the session change this.SendSessionChange(); // finally assign the correct packet handler this.Socket.SetReceiveCallback(ReceivePacketResponse); }
/// <summary> /// Utility method, creates a new pretty printer instance and dumps the given <paramref name="obj" /> /// </summary> /// <param name="obj">The Python type to dump</param> /// <returns></returns> public static string FromDataType(PyDataType obj) { PrettyPrinter printer = new PrettyPrinter(); printer.Process(obj); return(printer.GetResult()); }
/// <summary> /// Converts the given <paramref name="data" /> python type into a byte stream /// </summary> /// <param name="data">The Python type to convert into a byte stream</param> /// <param name="writeHeader">Whether the Marshal header has to be written or not</param> /// <returns>The full object converted into a byte stream</returns> public static byte[] ToByteArray(PyDataType data, bool writeHeader = true) { MemoryStream stream = new MemoryStream(); WriteToStream(stream, data, writeHeader); return(stream.ToArray()); }
public static PyCacheMethodCallResult FromCacheHint(PyDataType cacheInfo) { PyCacheMethodCallResult cachedObject = new PyCacheMethodCallResult(); cachedObject.CacheHint = cacheInfo; return(cachedObject); }
protected override BoundService CreateBoundInstance(PyDataType objectData, CallInformation call) { if (this.MachoResolveObject(objectData as PyTuple, 0, call) != this.BoundServiceManager.Container.NodeID) { throw new CustomError("Trying to bind an object that does not belong to us!"); } return(new dogmaIM(this.ItemFactory, this.BoundServiceManager)); }