diff --git a/src/commands/imc2.py b/src/commands/imc2.py index 13a05b0cff..5ec7156f12 100644 --- a/src/commands/imc2.py +++ b/src/commands/imc2.py @@ -21,4 +21,15 @@ def cmd_imctest(command): packet = IMC2PacketWhois(source_object, 'Cratylus') imc2_conn.IMC2_PROTOCOL_INSTANCE.send_packet(packet) source_object.emit_to("Sent") -GLOBAL_CMD_TABLE.add_command("imctest", cmd_imctest) \ No newline at end of file +GLOBAL_CMD_TABLE.add_command("imctest", cmd_imctest) + +def cmd_imckeepalive(command): + """ + Shows a player's inventory. + """ + source_object = command.source_object + source_object.emit_to("Sending") + packet = IMC2PacketIsAlive() + imc2_conn.IMC2_PROTOCOL_INSTANCE.send_packet(packet) + source_object.emit_to("Sent") +GLOBAL_CMD_TABLE.add_command("imckeepalive", cmd_imckeepalive) \ No newline at end of file diff --git a/src/imc2/connection.py b/src/imc2/connection.py index fdaee233dd..9b5a133ebb 100644 --- a/src/imc2/connection.py +++ b/src/imc2/connection.py @@ -25,6 +25,7 @@ class IMC2Protocol(StatefulTelnetProtocol): IMC2_PROTOCOL_INSTANCE = self self.is_authenticated = False self.auth_type = None + self.server_name = None self.network_name = None self.sequence = None @@ -61,9 +62,10 @@ class IMC2Protocol(StatefulTelnetProtocol): """ if line[:2] == "PW": line_split = line.split(' ') + self.server_name = line_split[1] self.network_name = line_split[4] self.is_authenticated = True - self.sequence = time.time() + self.sequence = int(time.time()) print "IMC2: Successfully authenticated to the '%s' network." % self.network_name def lineReceived(self, line): diff --git a/src/imc2/events.py b/src/imc2/events.py new file mode 100644 index 0000000000..4bbe80defc --- /dev/null +++ b/src/imc2/events.py @@ -0,0 +1,33 @@ +""" +This module contains all IMC2 events that are triggered periodically. +Most of these are used to maintain the existing connection and keep various +lists/caches up to date. +""" +from src import events +from src import scheduler +from src.imc2 import connection as imc2_conn +from src.imc2.packets import * + +class IEvt_IMC2_KeepAlive(events.IntervalEvent): + """ + Event: Send periodic keepalives to network neighbors. This lets the other + games know that our game is still up and connected to the network. Also + provides some useful information about the client game. + """ + name = 'IEvt_IMC2_KeepAlive' + # Send keep-alive packets every 15 minutes. + interval = 900 + description = "Send an IMC2 keepalive packet." + + def event_function(self): + """ + This is the function that is fired every self.interval seconds. + """ + packet = IMC2PacketIsAlive() + imc2_conn.IMC2_PROTOCOL_INSTANCE.send_packet(packet) + +def add_events(): + """ + Adds the IMC2 events to the scheduler. + """ + scheduler.add_event(IEvt_IMC2_KeepAlive()) \ No newline at end of file diff --git a/src/imc2/packets.py b/src/imc2/packets.py index e92d59396e..9046d8f220 100644 --- a/src/imc2/packets.py +++ b/src/imc2/packets.py @@ -19,7 +19,7 @@ class IMC2Packet(object): target = None destination = None # Optional data. - optional_data = {} + optional_data = [] # Reference to the IMC2Protocol object doing the sending. imc2_protocol = None @@ -29,8 +29,8 @@ class IMC2Packet(object): """ if self.optional_data: data_string = '' - for key, value in self.optional_data.items(): - self.data_string += '%s=%s ' % (key, value) + for value in self.optional_data: + data_string += '%s=%s ' % (value[0], value[1]) return data_string.strip() else: return '' @@ -39,13 +39,18 @@ class IMC2Packet(object): """ Calculates the sender name to be sent with the packet. """ - if self.sender: + if self.sender == '*': + # Some packets have no sender. + return '*' + elif self.sender: + # Player object. name = self.sender.get_name(fullname=False, show_dbref=False, show_flags=False, no_ansi=True) # IMC2 does not allow for spaces. return name.strip().replace(' ', '_') else: + # None value. Do something or other. return 'Unknown' def assemble(self): @@ -80,7 +85,51 @@ class IMC2PacketWhois(IMC2Packet): self.packet_type = 'whois' self.target = whois_target self.destination = '*' - self.data = {'level': '5'} + self.optional_data = [('level', '5')] + +class IMC2PacketIsAlive(IMC2Packet): + """ + Description: + This packet is the reply to a keepalive-request packet. It is responsible + for filling a client's mudlist with the information about other MUDs on the + network. + + Data: + versionid= + Where is the text version ID of the client. ("IMC2 4.5 MUD-Net") + + url= + Where is the proper URL of the client. (http://www.domain.com) + + host= + Where is the telnet address of the MUD. (telnet://domain.com) + + port= + Where is the telnet port of the MUD. + + (These data fields are not sent by the MUD, they are added by the server.) + networkname= + Where is the network name that the MUD/server is on. ("MyNetwork") + + sha256= + This is an optional tag that denotes the SHA-256 capabilities of a + MUD or server. + + Example of a received is-alive: + *@SomeMUD 1234567890 SomeMUD!Hub2 is-alive *@YourMUD versionid="IMC2 4.5 MUD-Net" url="http://www.domain.com" networkname="MyNetwork" sha256=1 host=domain.com port=5500 + + Example of a sent is-alive: + *@YourMUD 1234567890 YourMUD is-alive *@* versionid="IMC2 4.5 MUD-Net" url="http://www.domain.com" host=domain.com port=5500 + """ + def __init__(self): + self.sender = '*' + self.packet_type = 'is-alive' + self.target = '*' + self.destination = '*' + self.optional_data = [('versionid', '"Evennia IMC2"'), + ('url', '"http://evennia.com"'), + ('host', 'test.com'), + ('port', '5555')] class IMC2PacketAuthPlaintext(object): """ diff --git a/src/server.py b/src/server.py index d4fe1ca238..3df9827970 100755 --- a/src/server.py +++ b/src/server.py @@ -7,7 +7,6 @@ from django.db import connection from django.conf import settings from src.config.models import ConfigValue from src.session import SessionProtocol -from src.imc2.connection import IMC2ClientFactory from src import events from src import logger from src import session_mgr @@ -146,9 +145,12 @@ for port in settings.GAMEPORTS: if settings.IMC2_ENABLED: + from src.imc2.connection import IMC2ClientFactory + from src.imc2 import events as imc2_events imc2_factory = IMC2ClientFactory() svc = internet.TCPClient(settings.IMC2_SERVER_ADDRESS, settings.IMC2_SERVER_PORT, imc2_factory) svc.setName('IMC2') - svc.setServiceParent(serviceCollection) \ No newline at end of file + svc.setServiceParent(serviceCollection) + imc2_events.add_events() \ No newline at end of file