func player_walk(player player.Player, packet encoding.Decodable) { var walkPacket *game_protocol.InboundPlayerWalkBlock switch p := packet.(type) { case *game_protocol.InboundPlayerWalk: walkPacket = (*game_protocol.InboundPlayerWalkBlock)(p) case *game_protocol.InboundPlayerWalkMap: walkPacket = (*game_protocol.InboundPlayerWalkBlock)(p) default: panic(fmt.Sprintf("got invalid walk packet: %T", p)) } height := player.Position().Z() origin := position.NewAbsolute(int(walkPacket.OriginX), int(walkPacket.OriginY), height) waypoints := make([]*position.Absolute, len(walkPacket.Waypoints)) wpq := player.WaypointQueue() wpq.Clear() wpq.Push(origin) for i, wp := range walkPacket.Waypoints { x1, y1 := int(wp.X), int(wp.Y) x2, y2 := int(origin.X()), int(origin.Y()) waypoints[i] = position.NewAbsolute(int(x1+x2), int(y1+y2), height) wpq.Push(waypoints[i]) } }
// decodePacket decodes from the readBuffer using the ISAAC rand generator func (svc *GameService) decodePacket(client player.Player) error { b := client.Conn().ReadBuffer data, err := b.Peek(1) if err != nil { return err } idByte := int(data[0]) rand := client.ISAACIn().Rand() realId := uint8(uint32(idByte) - rand) packet, err := game_protocol.NewInboundPacket(int(realId)) if err != nil { return fmt.Errorf("%v: packet %v", err, realId) } err = packet.Decode(b, rand) if err != nil { return err } if !client.Conn().IsDisconnecting() { client.Conn().Read <- packet } return nil }
func Dispatch(player player.Player, packet encoding.Decodable) { typeString := reflect.TypeOf(packet).String() if handler, ok := routingTable[typeString]; ok { handler(player, packet) } else { player.Log().Info("Unhandled packet of type %v: %v", typeString, packet) } }
// decodeLoginBlock handles the unencrypted login block func (svc *GameService) decodeLoginBlock(client player.Player) error { loginBlock := game_protocol.InboundLoginBlock{} if err := loginBlock.Decode(client.Conn().ReadBuffer, nil); err != nil { return err } expectedSecureBlockSize := int(loginBlock.LoginLen) - ((9 * 4) + 1 + 1 + 1 + 2) if expectedSecureBlockSize != int(loginBlock.SecureBlockSize) { client.Log().Error("Secure block size mismatch: got %v expected %v", loginBlock.SecureBlockSize, expectedSecureBlockSize) client.Conn().Disconnect() } client.SetSecureBlockSize(int(loginBlock.SecureBlockSize)) client.SetDecodeFunc(svc.decodeSecureBlock) return nil }
// decodeSecureBlock handles the secure login block and the login response (via doLogin) func (svc *GameService) decodeSecureBlock(client player.Player) error { rsaBlock := encoding.RSABlock{&game_protocol.InboundSecureLoginBlock{}} rsaArgs := encoding.RSADecodeArgs{ Key: svc.key, BlockSize: client.SecureBlockSize(), } if err := rsaBlock.Decode(client.Conn().ReadBuffer, rsaArgs); err != nil { return err } secureBlock := rsaBlock.Codable.(*game_protocol.InboundSecureLoginBlock) // Seed the RNGs inSeed := make([]uint32, 4) outSeed := make([]uint32, 4) for i := range inSeed { inSeed[i] = uint32(secureBlock.ISAACSeed[i]) outSeed[i] = uint32(secureBlock.ISAACSeed[i]) + 50 } client.InitISAAC(inSeed, outSeed) username := string(secureBlock.Username) password := string(secureBlock.Password) password = auth.HashPassword(password) return svc.doLogin(client, username, password) }
// handshake performs the isaac key exchange func (svc *GameService) handshake(client player.Player) error { serverSeed := client.ServerISAACSeed() handshake := protocol.InboundGameHandshake{} if err := handshake.Decode(client.Conn().ReadBuffer, nil); err != nil { return err } client.Conn().Write <- &protocol.OutboundGameHandshake{ ServerISAACSeed: [2]encoding.Uint32{ encoding.Uint32(serverSeed[0]), encoding.Uint32(serverSeed[1]), }, } client.SetDecodeFunc(svc.decodeLoginBlock) return nil }
// packetConsumer is the goroutine which picks packets from the readQueue and does something with them func (svc *GameService) packetConsumer(client player.Player) { L: for { select { case <-client.Conn().DisconnectChan: break L case pkt := <-client.Conn().Read: if _, ok := pkt.(*game_protocol.UnknownPacket); ok { /* unknown packet; dump to the log */ client.Log().Debug("Got unknown packet: %v", pkt) continue } packet.Dispatch(client, pkt) } } }
func (struc *PlayerUpdateBlock) buildUpdateBlock(w io.Writer, thisPlayer player.Player) error { flags := thisPlayer.Flags() & ^entity.MobFlagMovementUpdate if flags == 0 { return nil } if flags >= 256 { flags |= 64 flagsEnc := encoding.Uint16(flags) err := flagsEnc.Encode(w, encoding.IntLittleEndian) if err != nil { return err } } else { flagsEnc := encoding.Uint8(flags) err := flagsEnc.Encode(w, encoding.IntNilFlag) if err != nil { return err } } /* Update appearance */ if (flags & entity.MobFlagIdentityUpdate) != 0 { buf := encoding.NewBuffer() appearance := thisPlayer.Profile().Appearance() anims := thisPlayer.Animations() appearanceBlock := OutboundPlayerAppearance{ Gender: encoding.Uint8(appearance.Gender()), HeadIcon: encoding.Uint8(appearance.HeadIcon()), HelmModel: encoding.Uint8(0), CapeModel: encoding.Uint8(0), AmuletModel: encoding.Uint8(0), RightWieldModel: encoding.Uint8(0), TorsoModel: encoding.Uint16(256 + appearance.Model(player.Torso)), LeftWieldModel: encoding.Uint8(0), ArmsModel: encoding.Uint16(256 + appearance.Model(player.Arms)), LegsModel: encoding.Uint16(256 + appearance.Model(player.Legs)), HeadModel: encoding.Uint16(256 + appearance.Model(player.Head)), HandsModel: encoding.Uint16(256 + appearance.Model(player.Hands)), FeetModel: encoding.Uint16(256 + appearance.Model(player.Feet)), BeardModel: encoding.Uint16(256 + appearance.Model(player.Beard)), HairColor: encoding.Uint8(appearance.Color(player.Hair)), TorsoColor: encoding.Uint8(appearance.Color(player.Torso)), LegColor: encoding.Uint8(appearance.Color(player.Legs)), FeetColor: encoding.Uint8(appearance.Color(player.Feet)), SkinColor: encoding.Uint8(appearance.Color(player.Skin)), AnimIdle: encoding.Uint16(anims.Animation(player.AnimIdle)), AnimSpotRotate: encoding.Uint16(anims.Animation(player.AnimSpotRotate)), AnimWalk: encoding.Uint16(anims.Animation(player.AnimWalk)), AnimRotate180: encoding.Uint16(anims.Animation(player.AnimRotate180)), AnimRotateCCW: encoding.Uint16(anims.Animation(player.AnimRotateCCW)), AnimRotateCW: encoding.Uint16(anims.Animation(player.AnimRotateCW)), AnimRun: encoding.Uint16(anims.Animation(player.AnimRun)), } err := appearanceBlock.Encode(buf, nil) if err != nil { return err } block := buf.Bytes() blockSize := encoding.Uint8(len(block)) err = blockSize.Encode(w, encoding.IntNegate) if err != nil { return err } _, err = w.Write(block) if err != nil { return err } } return nil }
// doLogin authenticates the user, sends the login response, and sets up the client for standard packet processing func (svc *GameService) doLogin(client player.Player, username, password string) error { profile, responseCode := svc.auth.LookupProfile(username, password) if responseCode != auth.AuthOkay { client.Conn().Write <- &game_protocol.OutboundLoginResponseUnsuccessful{ Response: encoding.Uint8(responseCode), } client.Conn().Disconnect() return nil } client.SetProfile(profile.(player.Profile)) // Successful login, do all the stuff client.Conn().Write <- &game_protocol.OutboundLoginResponse{ Response: encoding.Uint8(responseCode), Rights: encoding.Uint8(client.Profile().Rights()), Flagged: 0, } client.SetDecodeFunc(svc.decodePacket) go svc.packetConsumer(client) game_event.PlayerLogin.NotifyObservers(client) client.LoadProfile() game_event.PlayerFinishLogin.NotifyObservers(client) go func() { client.Conn().WaitForDisconnect() game_event.PlayerLogout.NotifyObservers(client) }() return nil }