diff --git a/src/events/character/connect.ts b/src/events/character/connect.ts
index 8fd3bdb..837444e 100644
--- a/src/events/character/connect.ts
+++ b/src/events/character/connect.ts
@@ -3,6 +3,7 @@ import Database from '#application/database'
 import ZoneManager from '#managers/zoneManager'
 import CharacterHairRepository from '#repositories/characterHairRepository'
 import CharacterRepository from '#repositories/characterRepository'
+import TeleportService from '#services/teleportService'
 
 interface CharacterConnectPayload {
   characterId: number
@@ -59,21 +60,14 @@ export default class CharacterConnectEvent extends BaseEvent {
       // wait 300 ms, @TODO: Find a better way to do this
       await new Promise(resolve => setTimeout(resolve, 100))
 
-      const zone = ZoneManager.getZoneById(character.zone!.id)
-      if (!zone) {
-        this.logger.error('zone:character:join error: Zone not found')
-        return
-      }
-
-      zone.addCharacter(character)
-
-      const zoneCharacter = ZoneManager.getCharacterById(character.id)
-      if (!zoneCharacter) {
-        this.logger.error('zone:character:join error: Zone character not found')
-        return
-      }
-
-      await zoneCharacter.teleport(character.zone!.id, character.positionX, character.positionY)
+      await TeleportService.teleportCharacter(character.id, {
+        targetZoneId: character.zone!.id,
+        targetX: character.positionX,
+        targetY: character.positionY,
+        rotation: character.rotation,
+        isInitialJoin: true,
+        character
+      })
     } catch (error) {
       this.handleError('Failed to connect character', error)
     }
diff --git a/src/events/chat/gameMaster/teleportCommand.ts b/src/events/chat/gameMaster/teleportCommand.ts
index 9886938..82039f8 100644
--- a/src/events/chat/gameMaster/teleportCommand.ts
+++ b/src/events/chat/gameMaster/teleportCommand.ts
@@ -1,9 +1,8 @@
 import { BaseEvent } from '#application/base/baseEvent'
 import ZoneManager from '#managers/zoneManager'
-import zoneManager from '#managers/zoneManager'
-import ZoneCharacter from '#models/zoneCharacter'
 import ZoneRepository from '#repositories/zoneRepository'
 import ChatService from '#services/chatService'
+import TeleportService from '#services/teleportService'
 
 type TypePayload = {
   message: string
@@ -14,9 +13,8 @@ export default class TeleportCommandEvent extends BaseEvent {
     this.socket.on('chat:message', this.handleEvent.bind(this))
   }
 
-  private async handleEvent(data: TypePayload, callback: (response: boolean) => void): Promise<void> {
+  private async handleEvent(data: TypePayload, callback: (response: boolean) => void) {
     try {
-      // Check if character exists
       const zoneCharacter = ZoneManager.getCharacterById(this.socket.characterId!)
       if (!zoneCharacter) {
         this.logger.error('chat:message error', 'Character not found')
@@ -25,7 +23,6 @@ export default class TeleportCommandEvent extends BaseEvent {
 
       const character = zoneCharacter.character
 
-      // Check if the user is the GM
       if (character.role !== 'gm') {
         this.logger.info(`User ${character.id} tried to set time but is not a game master.`)
         return
@@ -35,54 +32,68 @@ export default class TeleportCommandEvent extends BaseEvent {
 
       const args = ChatService.getArgs('teleport', data.message)
 
-      if (!args || args.length !== 1) {
-        this.socket.emit('notification', { title: 'Server message', message: 'Usage: /teleport <zoneId>' })
+      if (!args || args.length === 0 || args.length > 3) {
+        this.socket.emit('notification', {
+          title: 'Server message',
+          message: 'Usage: /teleport <zoneId> [x] [y]'
+        })
         return
       }
 
       const zoneId = parseInt(args[0], 10)
-      if (isNaN(zoneId)) {
-        this.socket.emit('notification', { title: 'Server message', message: 'Invalid zone ID' })
+      const targetX = args[1] ? parseInt(args[1], 10) : 0
+      const targetY = args[2] ? parseInt(args[2], 10) : 0
+
+      if (isNaN(zoneId) || isNaN(targetX) || isNaN(targetY)) {
+        this.socket.emit('notification', {
+          title: 'Server message',
+          message: 'Invalid parameters. All values must be numbers.'
+        })
         return
       }
 
       const zone = await ZoneRepository.getById(zoneId)
       if (!zone) {
-        this.socket.emit('notification', { title: 'Server message', message: 'Zone not found' })
+        this.socket.emit('notification', {
+          title: 'Server message',
+          message: 'Zone not found'
+        })
         return
       }
 
-      if (character.zoneId === zone.id) {
-        this.socket.emit('notification', { title: 'Server message', message: 'You are already in that zone' })
+      if (character.zone.id === zone.id && targetX === character.positionX && targetY === character.positionY) {
+        this.socket.emit('notification', {
+          title: 'Server message',
+          message: 'You are already at that location'
+        })
         return
       }
 
-      // Remove character from current zone
-      zoneManager.removeCharacter(character.id)
-      this.io.to(character.zoneId.toString()).emit('zone:character:leave', character.id)
-      this.socket.leave(character.zoneId.toString())
-
-      // Add character to new zone
-      zoneManager.getZoneById(zone.id)?.addCharacter(character)
-      this.io.to(zone.id.toString()).emit('zone:character:join', character)
-      this.socket.join(zone.id.toString())
-
-      character.zoneId = zone.id
-      character.positionX = 0
-      character.positionY = 0
-
-      zoneCharacter.isMoving = false
-
-      this.socket.emit('zone:character:teleport', {
-        zone,
-        characters: ZoneManager.getZoneById(zone.id)?.getCharactersInZone()
+      const success = await TeleportService.teleportCharacter(character.id, {
+        targetZoneId: zone.id,
+        targetX,
+        targetY,
+        rotation: character.rotation
       })
 
-      this.socket.emit('notification', { title: 'Server message', message: `You have been teleported to ${zone.name}` })
-      this.logger.info('teleport', `Character ${character.id} teleported to zone ${zone.id}`)
+      if (!success) {
+        return this.socket.emit('notification', {
+          title: 'Server message',
+          message: 'Failed to teleport'
+        })
+      }
+
+      this.socket.emit('notification', {
+        title: 'Server message',
+        message: `Teleported to ${zone.name} (${targetX}, ${targetY})`
+      })
+      this.logger.info('teleport', `Character ${character.id} teleported to zone ${zone.id} at position (${targetX}, ${targetY})`)
     } catch (error: any) {
       this.logger.error(`Error in teleport command: ${error.message}`)
-      this.socket.emit('notification', { title: 'Server message', message: 'An error occurred while teleporting' })
+      this.socket.emit('notification', {
+        title: 'Server message',
+        message: 'An error occurred while teleporting'
+      })
     }
   }
-}
+}
\ No newline at end of file
diff --git a/src/events/disconnect.ts b/src/events/disconnect.ts
index c159361..694ca8f 100644
--- a/src/events/disconnect.ts
+++ b/src/events/disconnect.ts
@@ -6,7 +6,7 @@ export default class DisconnectEvent extends BaseEvent {
     this.socket.on('disconnect', this.handleEvent.bind(this))
   }
 
-  private async handleEvent(data: any): Promise<void> {
+  private async handleEvent(): Promise<void> {
     try {
       if (!this.socket.userId) {
         this.logger.info('User disconnected but had no user set')
@@ -21,20 +21,10 @@ export default class DisconnectEvent extends BaseEvent {
         return
       }
 
-      const character = zoneCharacter.character
-
-      // Save character position and remove from zone
-      zoneCharacter.isMoving = false
-      await zoneCharacter.savePosition()
-      ZoneManager.removeCharacter(this.socket.characterId!)
-
+      await zoneCharacter.disconnect(this.socket, this.io)
       this.logger.info('User disconnected along with their character')
-
-      // Inform other clients that the character has left
-      this.io.in(character.zone!.id.toString()).emit('zone:character:leave', character.id)
-      this.io.emit('character:disconnect', character.id)
     } catch (error: any) {
       this.logger.error('disconnect error: ' + error.message)
     }
   }
-}
+}
\ No newline at end of file
diff --git a/src/models/zoneCharacter.ts b/src/models/zoneCharacter.ts
index 8bfde71..bee68c2 100644
--- a/src/models/zoneCharacter.ts
+++ b/src/models/zoneCharacter.ts
@@ -1,6 +1,6 @@
 import { Character } from '#entities/character'
+import TeleportService from '#services/teleportService'
 import ZoneManager from '#managers/zoneManager'
-import Logger, { LoggerType } from '#application/logger'
 import SocketManager from '#managers/socketManager'
 
 class ZoneCharacter {
@@ -13,44 +13,44 @@ class ZoneCharacter {
   }
 
   public async savePosition() {
-    await this.character.setPositionX(this.character.positionX).setPositionY(this.character.positionY).setRotation(this.character.rotation).setZone(this.character.zone).update()
+    await this.character
+      .setPositionX(this.character.positionX)
+      .setPositionY(this.character.positionY)
+      .setRotation(this.character.rotation)
+      .setZone(this.character.zone)
+      .update()
   }
 
   public async teleport(zoneId: number, targetX: number, targetY: number): Promise<void> {
-    const io = SocketManager.getIO()
-    const socket = SocketManager.getSocketByCharacterId(this.character.id)
-    const zone = ZoneManager.getZoneById(zoneId)?.getZone()
-    const logger = Logger.type(LoggerType.APP)
-    console.log('teleporting')
-
-    if (!socket) {
-      logger.error('zone:character:move error: Socket not found')
-      return
-    }
-
-    if (!zone) {
-      logger.error('zone:character:move error: Zone not found')
-      return
-    }
-
-    // Let other clients know of new character
-    io.to(zone.id.toString()).emit('zone:character:join', 'ewaewa')
-
-    // Update zoneCharacter properties
-    this.currentPath = null
-    this.isMoving = false
-
-    // Update local character object
-    await this.character.setPositionX(targetX).setPositionY(targetY).setRotation(this.character.rotation).setZone(zone).update()
-
-    // Emit teleport event
-    socket.emit('zone:character:teleport', {
-      zone,
-      characters: ZoneManager.getZoneById(zone.id)?.getCharactersInZone()
+    await TeleportService.teleportCharacter(this.character.id, {
+      targetZoneId: zoneId,
+      targetX,
+      targetY
     })
+  }
 
-    console.log('teleported')
+  public async disconnect(socket: Socket, io: Server): Promise<void> {
+    try {
+      // Stop any movement and save final position
+      this.isMoving = false
+      this.currentPath = null
+      await this.savePosition()
+
+      // Leave zone and remove from manager
+      if (this.character.zone) {
+        socket.leave(this.character.zone.id.toString())
+        ZoneManager.removeCharacter(this.character.id)
+
+        // Notify zone players
+        io.in(this.character.zone.id.toString()).emit('zone:character:leave', this.character.id)
+      }
+
+      // Notify all players
+      io.emit('character:disconnect', this.character.id)
+    } catch (error) {
+      console.error(`Error disconnecting character ${this.character.id}:`, error)
+    }
   }
 }
 
-export default ZoneCharacter
+export default ZoneCharacter
\ No newline at end of file
diff --git a/src/services/teleportService.ts b/src/services/teleportService.ts
new file mode 100644
index 0000000..97d4bea
--- /dev/null
+++ b/src/services/teleportService.ts
@@ -0,0 +1,83 @@
+import { Character } from '#entities/character'
+import ZoneManager from '#managers/zoneManager'
+import SocketManager from '#managers/socketManager'
+import Logger, { LoggerType } from '#application/logger'
+import ZoneCharacter from '#models/zoneCharacter'
+
+interface TeleportOptions {
+  targetZoneId: number
+  targetX: number
+  targetY: number
+  rotation?: number
+  isInitialJoin?: boolean
+  character?: Character
+}
+
+class TeleportService {
+  private readonly logger = Logger.type(LoggerType.GAME)
+
+  public async teleportCharacter(characterId: number, options: TeleportOptions): Promise<boolean> {
+    const { targetZoneId, targetX, targetY, rotation = 0, isInitialJoin = false, character } = options
+
+    const socket = SocketManager.getSocketByCharacterId(characterId)
+    const targetZone = ZoneManager.getZoneById(targetZoneId)
+
+    if (!socket || !targetZone) {
+      this.logger.error(`Teleport failed - Missing socket or target zone for character ${characterId}`)
+      return false
+    }
+
+    if (isInitialJoin && !character) {
+      this.logger.error('Initial join requires character data')
+      return false
+    }
+
+    const existingCharacter = !isInitialJoin && ZoneManager.getCharacterById(characterId)
+    const zoneCharacter = isInitialJoin
+      ? new ZoneCharacter(character!)
+      : existingCharacter || (() => {
+      this.logger.error(`Teleport failed - Character ${characterId} not found in ZoneManager`)
+      return null
+    })()
+
+    if (!zoneCharacter) return false
+
+    try {
+      const currentZoneId = zoneCharacter.character.zone?.id
+      const io = SocketManager.getIO()
+
+      // Handle current zone cleanup
+      if (currentZoneId) {
+        socket.leave(currentZoneId.toString())
+        ZoneManager.removeCharacter(characterId)
+        io.in(currentZoneId.toString()).emit('zone:character:leave', characterId)
+      }
+
+      // Update character position and zone
+      await zoneCharacter.character
+        .setPositionX(targetX)
+        .setPositionY(targetY)
+        .setRotation(rotation)
+        .setZone(targetZone.getZone())
+        .update()
+
+      // Join new zone
+      socket.join(targetZoneId.toString())
+      targetZone.addCharacter(zoneCharacter.character)
+
+      // Notify clients
+      io.in(targetZoneId.toString()).emit('zone:character:join', zoneCharacter)
+      socket.emit('zone:character:teleport', {
+        zone: targetZone.getZone(),
+        characters: targetZone.getCharactersInZone()
+      })
+
+      return true
+    } catch (error) {
+      this.logger.error(`Teleport error for character ${characterId}: ${error}`)
+      return false
+    }
+  }
+}
+
+export default new TeleportService()
\ No newline at end of file