class SimpleClient(ShowBase):
    def __init__(self):
        # Basics
        ShowBase.__init__(self)
        base.disableMouse()
        self.accept("escape", self.disconnect)
        base.camera.set_pos(0, 0, 60)
        base.camera.look_at(0, 0, 0)
        # Game-relevant attributes
        self.has_avatar = False
        self.avatar_owner_view = False
        # Avatar controls
        # FIXME: These values will be off the kilter if keys are pressed when the client starts.
        self.movement_heading = 0
        self.movement_speed = 0
        self.accept("avatar", self.get_avatar)
        self.accept("distributed_avatar", self.get_distributed_avatar)
        self.accept("arrow_up", self.indicate_movement, [0, 1])
        self.accept("arrow_up-up", self.indicate_movement, [0, -1])
        self.accept("arrow_down", self.indicate_movement, [0, -1])
        self.accept("arrow_down-up", self.indicate_movement, [0, 1])
        self.accept("arrow_left", self.indicate_movement, [1, 0])
        self.accept("arrow_left-up", self.indicate_movement, [-1, 0])
        self.accept("arrow_right", self.indicate_movement, [-1, 0])
        self.accept("arrow_right-up", self.indicate_movement, [1, 0])
        # Create repository
        # FIXME: For some freaky reason, using the default method
        # (CM_HTTP) won't result in further callbacks being called
        # back. Does connection() fail?
        self.repo = AstronClientRepository(
            dcFileNames=["simple_example.dc"],
            connectMethod=AstronClientRepository.CM_NET)
        # Callback events. These names are "magic" (defined in AstronClientRepository)
        self.accept("CLIENT_HELLO_RESP", self.client_is_handshaked)
        self.accept("CLIENT_EJECT", self.ejected)
        self.accept("CLIENT_OBJECT_LEAVING", self.avatar_leaves)
        self.accept("CLIENT_OBJECT_LEAVING_OWNER", self.avatar_leaves_owner)
        self.accept("LOST_CONNECTION", self.lost_connection)
        # Connecting
        url = URLSpec()
        url.setServer("127.0.0.1")
        url.setPort(6667)
        # FIXME: No idea why this doesn't work instead...
        # url = URLSpec("127.0.0.1", 6667)
        self.notify.debug("Connecting...")
        self.repo.connect([url],
                          successCallback=self.connection_success,
                          failureCallback=self.connection_failure)

    #
    # Connection management (callbacks and helpers)
    #

    # Connection established. Send CLIENT_HELLO to progress from NEW to UNKNOWN.
    # Normally, there could be code here for things to do before entering making
    # the connection and actually interacting with the server.
    def connection_success(self, *args):
        self.repo.sendHello(version_string)

    def connection_failure(self):
        self.notify.error("Failed to connect")
        sys.exit()

    def lost_connection(self):
        self.notify.error("Lost connection")
        sys.exit()

    # Voluntarily end the connection.
    def disconnect(self):
        self.repo.disconnect()
        sys.exit()

    # Client was ejected
    def ejected(self, error_code, reason):
        self.notify.error("Ejected! %d: %s" % (error_code, reason))
        sys.exit()

    # Client has received CLIENT_HELLO_RESP and now is in state UNKNOWN.
    def client_is_handshaked(self, *args):
        login_manager = self.repo.generateGlobalObject(1234, 'LoginManager')
        # Attach map to scene graph
        self.map = self.loader.loadModel("map")
        self.map.reparent_to(self.render)
        # Log in and receive; leads to enter_owner (ownership of avatar)
        login_manager.login("guest", "guest")

    def avatar_leaves(self, do_id):
        print("Avatar leaving: " + str(do_id))

    def avatar_leaves_owner(self, do_id):
        print("AvatarOV leaving: " + str(do_id))

    #
    # Interface
    #

    # Adjust current intention and send it.
    def indicate_movement(self, heading, speed):
        if self.has_avatar:
            # FIXME: Not really graceful to just ignore this.
            # What if a button was already pressed when we got the OV?

            self.movement_heading += heading
            self.movement_speed += speed
            self.avatar_owner_view.indicateIntent(self.movement_heading,
                                                  self.movement_speed)
        else:
            print("Avatar not complete yet!")

    # A DistributedAvatarOV was created, here is it.
    def get_avatar(self, owner_view):
        print("Received avatar OV in client")
        self.avatar_owner_view = owner_view
        self.taskMgr.add(self.complete_avatar, 'complete avatar')

    def complete_avatar(self, task):
        try:
            avatar = self.repo.doId2do[self.avatar_owner_view.doId]
            base.camera.reparent_to(avatar)
            base.camera.set_pos(0, -20, 10)
            base.camera.look_at(0, 0, 0)
            self.has_avatar = True
        except KeyError:
            print("Couldn't complete avatar " +
                  str(self.avatar_owner_view.doId) + ", available DOs: " +
                  ", ".join([str(doId) for doId in self.repo.doId2do.keys()]))
            return Task.cont

    # A DistributedAvatar was created, here is it.
    def get_distributed_avatar(self, avatar):
        print("Received avatar " + str(avatar.doId))
        avatar.reparent_to(self.map)
 def handleConnected(self):
     self.notify.info("Sending CLIENT_HELLO...")
     self.acceptOnce("CLIENT_HELLO_RESP", self.handleClientHelloResp)
     self.acceptOnce("CLIENT_EJECT", self.handleEjected)
     self.acceptOnce("LOST_CONNECTION", self.handleLostConnection)
     AstronClientRepository.sendHello(self, self.serverVersion)
class SimpleClient(ShowBase):
    def __init__(self):
        # Basics
        ShowBase.__init__(self)
        base.disableMouse()
        self.accept("escape", self.disconnect)
        base.camera.set_pos(0, 0, 60)
        base.camera.look_at(0, 0, 0)
        # Game-relevant attributes
        self.has_avatar = False
        self.avatar_owner_view = False
        # Avatar controls
        # FIXME: These values will be off the kilter if keys are pressed when the client starts.
        self.movement_heading = 0
        self.movement_speed = 0
        self.accept("avatar", self.get_avatar)
        self.accept("distributed_avatar", self.get_distributed_avatar)
        self.accept("arrow_up", self.indicate_movement, [0, 1])
        self.accept("arrow_up-up", self.indicate_movement, [0, -1])
        self.accept("arrow_down", self.indicate_movement, [0, -1])
        self.accept("arrow_down-up", self.indicate_movement, [0, 1])
        self.accept("arrow_left", self.indicate_movement, [1, 0])
        self.accept("arrow_left-up", self.indicate_movement, [-1, 0])
        self.accept("arrow_right", self.indicate_movement, [-1, 0])
        self.accept("arrow_right-up", self.indicate_movement, [1, 0])
        # Create repository
        # FIXME: For some freaky reason, using the default method
        # (CM_HTTP) won't result in further callbacks being called
        # back. Does connection() fail?
        self.repo = AstronClientRepository(dcFileNames = ["simple_example.dc"],
                                           connectMethod = AstronClientRepository.CM_NET)
        # Callback events. These names are "magic" (defined in AstronClientRepository)
        self.accept("CLIENT_HELLO_RESP", self.client_is_handshaked)
        self.accept("CLIENT_EJECT", self.ejected)
        self.accept("CLIENT_OBJECT_LEAVING", self.avatar_leaves)
        self.accept("CLIENT_OBJECT_LEAVING_OWNER", self.avatar_leaves_owner)
        self.accept("LOST_CONNECTION", self.lost_connection)
        # Connecting
        url = URLSpec()
        url.setServer("127.0.0.1")
        url.setPort(6667)
        # FIXME: No idea why this doesn't work instead...
        # url = URLSpec("127.0.0.1", 6667)
        self.notify.debug("Connecting...")
        self.repo.connect([url],
                          successCallback = self.connection_success,
                          failureCallback = self.connection_failure)

    #
    # Connection management (callbacks and helpers)
    #

    # Connection established. Send CLIENT_HELLO to progress from NEW to UNKNOWN.
    # Normally, there could be code here for things to do before entering making
    # the connection and actually interacting with the server.
    def connection_success(self, *args):
        self.repo.sendHello(version_string)

    def connection_failure(self):
        self.notify.error("Failed to connect")
        sys.exit()

    def lost_connection(self):
        self.notify.error("Lost connection")
        sys.exit()

    # Voluntarily end the connection.
    def disconnect(self):
        self.repo.disconnect()
        sys.exit()
    
    # Client was ejected
    def ejected(self, error_code, reason):
        self.notify.error("Ejected! %d: %s" % (error_code, reason))
        sys.exit()

    # Client has received CLIENT_HELLO_RESP and now is in state UNKNOWN.
    def client_is_handshaked(self, *args):
        login_manager = self.repo.generateGlobalObject(1234, 'LoginManager')
        # Attach map to scene graph
        self.map = self.loader.loadModel("map")
        self.map.reparent_to(self.render)
        # Log in and receive; leads to enter_owner (ownership of avatar)
        login_manager.login("guest", "guest")

    def avatar_leaves(self, do_id):
        print("Avatar leaving: "+str(do_id))

    def avatar_leaves_owner(self, do_id):
        print("AvatarOV leaving: "+str(do_id))
        
    #
    # Interface
    #
    
    # Adjust current intention and send it.
    def indicate_movement(self, heading, speed):
        if self.has_avatar:
            # FIXME: Not really graceful to just ignore this.
            # What if a button was already pressed when we got the OV?
                        
            self.movement_heading += heading
            self.movement_speed += speed
            self.avatar_owner_view.indicateIntent(self.movement_heading, self.movement_speed)
        else:
            print("Avatar not complete yet!")

    # A DistributedAvatarOV was created, here is it.
    def get_avatar(self, owner_view):
        print("Received avatar OV in client")
        self.avatar_owner_view = owner_view
        self.taskMgr.add(self.complete_avatar, 'complete avatar')

    def complete_avatar(self, task):
        try:
            avatar = self.repo.doId2do[self.avatar_owner_view.doId]
            base.camera.reparent_to(avatar)
            base.camera.set_pos(0, -20, 10)
            base.camera.look_at(0, 0, 0)
            self.has_avatar = True
        except KeyError:
            print("Couldn't complete avatar "+str(self.avatar_owner_view.doId)+", available DOs: "+", ".join([str(doId) for doId in self.repo.doId2do.keys()]))
            return Task.cont

    # A DistributedAvatar was created, here is it.
    def get_distributed_avatar(self, avatar):
        print("Received avatar "+str(avatar.doId))
        avatar.reparent_to(self.map)