Skip to content

The client randomly disconnects after a quick world switch. #853

Closed
@Dasciam

Description

@Dasciam

What happened?

When a player moves from one world to another very quickly, when all the chunks in that world have not yet been sent, the client randomly disconnects with a message:

image

Also, the client does not actually disconnect and continues to receive packets from the server on the disconnect screen...

Code used to reproduce the issue:

main.go:

package main

import (
	"fmt"
	"github.com/df-mc/dragonfly/server"
	"github.com/df-mc/dragonfly/server/block"
	"github.com/df-mc/dragonfly/server/block/cube"
	"github.com/df-mc/dragonfly/server/player"
	"github.com/df-mc/dragonfly/server/player/chat"
	"github.com/df-mc/dragonfly/server/world"
	"github.com/pelletier/go-toml"
	"github.com/sirupsen/logrus"
	"math/rand"
	"os"
	"time"
)

// sleep sleeps for five ticks.
// For the bug showcase.
// - Sleep for five ticks, to imitate user's manual fast world switch.
func sleep() {
	time.Sleep(time.Second / 20 * 5)
}

// prepareWorld prepares the world for bug showcase:
// - Fills area with random block sequence;
// - Stops all world events like time and weather.
func prepareWorld(w *world.World) {
	const (
		randBoundXZ = 50
		randBoundY
	)

	for x := -randBoundXZ; x < randBoundXZ; x++ {
		for z := -randBoundXZ; z < randBoundXZ; z++ {
			for y := 0; y < randBoundY; y++ {
				var b world.Block = block.Bedrock{}
				if rand.Int()%5 == 2 {
					b = block.Stone{}
				}

				w.SetBlock(cube.Pos{x, y, z}, b, nil)
			}
		}
	}
	w.SetSpawn(cube.Pos{0, randBoundY + 5, 0})

	w.SetTime(6000)
	w.StopTime()

	w.StopThundering()
	w.StopRaining()
	w.StopWeatherCycle()
}

func main() {
	log := logrus.New()
	log.Formatter = &logrus.TextFormatter{ForceColors: true}
	log.Level = logrus.DebugLevel

	chat.Global.Subscribe(chat.StdoutSubscriber{})

	conf, err := readConfig(log)
	if err != nil {
		log.Fatalln(err)
	}

	srv := conf.New()
	srv.CloseOnProgramEnd()

	// Create two temporary worlds.
	w1 := world.Config{
		Generator:       world.NopGenerator{},
		RandomTickSpeed: 0,
		Entities:        world.EntityRegistry{},
	}.New()
	prepareWorld(w1)

	w2 := world.Config{
		Generator:       world.NopGenerator{},
		RandomTickSpeed: 0,
		Entities:        world.EntityRegistry{},
	}.New()
	prepareWorld(w2)

	srv.Listen()
	for srv.Accept(func(p *player.Player) {
		go func() {
			for i := 0; i < 10; i++ {
				w1.AddEntity(p)
				p.Teleport(w1.Spawn().Vec3Centre())

				sleep()

				w2.AddEntity(p)
				p.Teleport(w2.Spawn().Vec3Centre())

				sleep()
			}
		}()
	}) {
	}
}

// readConfig reads the configuration from the config.toml file, or creates the
// file if it does not yet exist.
func readConfig(log server.Logger) (server.Config, error) {
	c := server.DefaultConfig()
	var zero server.Config
	if _, err := os.Stat("config.toml"); os.IsNotExist(err) {
		data, err := toml.Marshal(c)
		if err != nil {
			return zero, fmt.Errorf("encode default config: %v", err)
		}
		if err := os.WriteFile("config.toml", data, 0644); err != nil {
			return zero, fmt.Errorf("create default config: %v", err)
		}
		return c.Config(log)
	}
	data, err := os.ReadFile("config.toml")
	if err != nil {
		return zero, fmt.Errorf("read config: %v", err)
	}
	if err := toml.Unmarshal(data, &c); err != nil {
		return zero, fmt.Errorf("decode config: %v", err)
	}
	return c.Config(log)
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions