<commit_before>package game

import (
	"fmt"
	"os"

	"time"

	"io/ioutil"

	"github.com/xiaonanln/go-xnsyncutil/xnsyncutil"
	timer "github.com/xiaonanln/goTimer"
	"github.com/xiaonanln/goworld/common"
	"github.com/xiaonanln/goworld/components/dispatcher/dispatcher_client"
	"github.com/xiaonanln/goworld/config"
	"github.com/xiaonanln/goworld/consts"
	"github.com/xiaonanln/goworld/entity"
	"github.com/xiaonanln/goworld/gwlog"
	"github.com/xiaonanln/goworld/kvdb"
	"github.com/xiaonanln/goworld/netutil"
	"github.com/xiaonanln/goworld/post"
	"github.com/xiaonanln/goworld/proto"
)

const (
	rsNotRunning = iota
	rsRunning
	rsTerminating
	rsTerminated
	rsFreezing
	rsFreezed
)

type packetQueueItem struct { // packet queue from dispatcher client
	msgtype proto.MsgType_t
	packet  *netutil.Packet
}

type GameService struct {
	config       *config.GameConfig
	id           uint16
	gameDelegate IGameDelegate
	//registeredServices map[string]entity.EntityIDSet

	packetQueue         chan packetQueueItem
	isAllGamesConnected bool
	runState            xnsyncutil.AtomicInt
	//collectEntitySyncInfosRequest chan struct{}
	//collectEntitySycnInfosReply   chan interface{}
}

func newGameService(gameid uint16, delegate IGameDelegate) *GameService {
	return &GameService{
		id:           gameid,
		gameDelegate: delegate,
		//registeredServices: map[string]entity.EntityIDSet{},
		packetQueue: make(chan packetQueueItem, consts.GAME_SERVICE_PACKET_QUEUE_SIZE),
		//terminated:         xnsyncutil.NewOneTimeCond(),
		//dumpNotify:         xnsyncutil.NewOneTimeCond(),
		//dumpFinishedNotify: xnsyncutil.NewOneTimeCond(),
		//collectEntitySyncInfosRequest: make(chan struct{}),
		//collectEntitySycnInfosReply:   make(chan interface{}),
	}
}

func (gs *GameService) run(restore bool) {
	gs.runState.Store(rsRunning)

	if !restore {
		entity.CreateSpaceLocally(0) // create to be the nil space
	} else {
		// restoring from freezed states
		err := gs.doRestore()
		if err != nil {
			gwlog.Fatal("Restore from freezed states failed: %+v", err)
		}
	}

	netutil.ServeForever(gs.serveRoutine)
}

func (gs *GameService) serveRoutine() {
	cfg := config.GetGame(gameid)
	gs.config = cfg
	gwlog.Info("Read game %d config: \n%s\n", gameid, config.DumpPretty(cfg))

	ticker := time.Tick(consts.GAME_SERVICE_TICK_INTERVAL)
	// here begins the main loop of Game
	for {
		select {
		case item := <-gs.packetQueue:
			msgtype, pkt := item.msgtype, item.packet
			if msgtype == proto.MT_SYNC_POSITION_YAW_FROM_CLIENT {
				gs.HandleSyncPositionYawFromClient(pkt)
			} else if msgtype == proto.MT_CALL_ENTITY_METHOD_FROM_CLIENT {
				eid := pkt.ReadEntityID()
				method := pkt.ReadVarStr()
				args := pkt.ReadArgs()
				clientid := pkt.ReadClientID()
				gs.HandleCallEntityMethod(eid, method, args, clientid)
			} else if msgtype == proto.MT_CALL_ENTITY_METHOD {
				eid := pkt.ReadEntityID()
				method := pkt.ReadVarStr()
				args := pkt.ReadArgs()
				gs.HandleCallEntityMethod(eid, method, args, "")
			} else if msgtype == proto.MT_MIGRATE_REQUEST { // migrate request sent to dispatcher is sent back
				gs.HandleMigrateRequestAck(pkt)
			} else if msgtype == proto.MT_REAL_MIGRATE {
				gs.HandleRealMigrate(pkt)
			} else if msgtype == proto.MT_NOTIFY_CLIENT_CONNECTED {
				clientid := pkt.ReadClientID()
				gid := pkt.ReadUint16()
				gs.HandleNotifyClientConnected(clientid, gid)
			} else if msgtype == proto.MT_NOTIFY_CLIENT_DISCONNECTED {
				clientid := pkt.ReadClientID()
				gs.HandleNotifyClientDisconnected(clientid)
			} else if msgtype == proto.MT_LOAD_ENTITY_ANYWHERE {
				eid := pkt.ReadEntityID()
				typeName := pkt.ReadVarStr()
				gs.HandleLoadEntityAnywhere(typeName, eid)
			} else if msgtype == proto.MT_CREATE_ENTITY_ANYWHERE {
				typeName := pkt.ReadVarStr()
				var data map[string]interface{}
				pkt.ReadData(&data)
				gs.HandleCreateEntityAnywhere(typeName, data)
			} else if msgtype == proto.MT_DECLARE_SERVICE {
				eid := pkt.ReadEntityID()
				serviceName := pkt.ReadVarStr()
				gs.HandleDeclareService(eid, serviceName)
			} else if msgtype == proto.MT_UNDECLARE_SERVICE {
				eid := pkt.ReadEntityID()
				serviceName := pkt.ReadVarStr()
				gs.HandleUndeclareService(eid, serviceName)
			} else if msgtype == proto.MT_NOTIFY_ALL_GAMES_CONNECTED {
				gs.HandleNotifyAllGamesConnected()
			} else if msgtype == proto.MT_NOTIFY_GATE_DISCONNECTED {
				gateid := pkt.ReadUint16()
				gs.HandleGateDisconnected(gateid)
			} else if msgtype == proto.MT_START_FREEZE_GAME_ACK {
				gs.HandleStartFreezeGameAck()
			} else {
				gwlog.TraceError("unknown msgtype: %v", msgtype)
				if consts.DEBUG_MODE {
					os.Exit(2)
				}
			}

			pkt.Release()
		case <-ticker:
			runState := gs.runState.Load()
			if runState == rsTerminating {
				// game is terminating, run the terminating process
				gs.doTerminate()
			} else if runState == rsFreezing {
				//game is freezing, run freeze process
				gs.doFreeze()
			}

			timer.Tick()

			gameDispatcherClientDelegate.HandleDispatcherClientBeforeFlush()
			dispatcher_client.GetDispatcherClientForSend().Flush()
			//case <-gs.collectEntitySyncInfosRequest: //
			//	gs.collectEntitySycnInfosReply <- 1
		}

		// after handling packets or firing timers, check the posted functions
		post.Tick()
	}
}

func (gs *GameService) waitPostsComplete() {
	post.Tick() // just tick is Ok, tick will consume all posts
}

func (gs *GameService) doTerminate() {
	// wait for all posts to complete<commit_msg>let gameservice flush after tick & post<commit_after>package game

import (
	"fmt"
	"os"

	"time"

	"io/ioutil"

	"github.com/xiaonanln/go-xnsyncutil/xnsyncutil"
	timer "github.com/xiaonanln/goTimer"
	"github.com/xiaonanln/goworld/common"
	"github.com/xiaonanln/goworld/components/dispatcher/dispatcher_client"
	"github.com/xiaonanln/goworld/config"
	"github.com/xiaonanln/goworld/consts"
	"github.com/xiaonanln/goworld/entity"
	"github.com/xiaonanln/goworld/gwlog"
	"github.com/xiaonanln/goworld/kvdb"
	"github.com/xiaonanln/goworld/netutil"
	"github.com/xiaonanln/goworld/post"
	"github.com/xiaonanln/goworld/proto"
)

const (
	rsNotRunning = iota
	rsRunning
	rsTerminating
	rsTerminated
	rsFreezing
	rsFreezed
)

type packetQueueItem struct { // packet queue from dispatcher client
	msgtype proto.MsgType_t
	packet  *netutil.Packet
}

type GameService struct {
	config       *config.GameConfig
	id           uint16
	gameDelegate IGameDelegate
	//registeredServices map[string]entity.EntityIDSet

	packetQueue         chan packetQueueItem
	isAllGamesConnected bool
	runState            xnsyncutil.AtomicInt
	//collectEntitySyncInfosRequest chan struct{}
	//collectEntitySycnInfosReply   chan interface{}
}

func newGameService(gameid uint16, delegate IGameDelegate) *GameService {
	return &GameService{
		id:           gameid,
		gameDelegate: delegate,
		//registeredServices: map[string]entity.EntityIDSet{},
		packetQueue: make(chan packetQueueItem, consts.GAME_SERVICE_PACKET_QUEUE_SIZE),
		//terminated:         xnsyncutil.NewOneTimeCond(),
		//dumpNotify:         xnsyncutil.NewOneTimeCond(),
		//dumpFinishedNotify: xnsyncutil.NewOneTimeCond(),
		//collectEntitySyncInfosRequest: make(chan struct{}),
		//collectEntitySycnInfosReply:   make(chan interface{}),
	}
}

func (gs *GameService) run(restore bool) {
	gs.runState.Store(rsRunning)

	if !restore {
		entity.CreateSpaceLocally(0) // create to be the nil space
	} else {
		// restoring from freezed states
		err := gs.doRestore()
		if err != nil {
			gwlog.Fatal("Restore from freezed states failed: %+v", err)
		}
	}

	netutil.ServeForever(gs.serveRoutine)
}

func (gs *GameService) serveRoutine() {
	cfg := config.GetGame(gameid)
	gs.config = cfg
	gwlog.Info("Read game %d config: \n%s\n", gameid, config.DumpPretty(cfg))

	ticker := time.Tick(consts.GAME_SERVICE_TICK_INTERVAL)
	// here begins the main loop of Game
	for {
		isTick := false
		select {
		case item := <-gs.packetQueue:
			msgtype, pkt := item.msgtype, item.packet
			if msgtype == proto.MT_SYNC_POSITION_YAW_FROM_CLIENT {
				gs.HandleSyncPositionYawFromClient(pkt)
			} else if msgtype == proto.MT_CALL_ENTITY_METHOD_FROM_CLIENT {
				eid := pkt.ReadEntityID()
				method := pkt.ReadVarStr()
				args := pkt.ReadArgs()
				clientid := pkt.ReadClientID()
				gs.HandleCallEntityMethod(eid, method, args, clientid)
			} else if msgtype == proto.MT_CALL_ENTITY_METHOD {
				eid := pkt.ReadEntityID()
				method := pkt.ReadVarStr()
				args := pkt.ReadArgs()
				gs.HandleCallEntityMethod(eid, method, args, "")
			} else if msgtype == proto.MT_MIGRATE_REQUEST { // migrate request sent to dispatcher is sent back
				gs.HandleMigrateRequestAck(pkt)
			} else if msgtype == proto.MT_REAL_MIGRATE {
				gs.HandleRealMigrate(pkt)
			} else if msgtype == proto.MT_NOTIFY_CLIENT_CONNECTED {
				clientid := pkt.ReadClientID()
				gid := pkt.ReadUint16()
				gs.HandleNotifyClientConnected(clientid, gid)
			} else if msgtype == proto.MT_NOTIFY_CLIENT_DISCONNECTED {
				clientid := pkt.ReadClientID()
				gs.HandleNotifyClientDisconnected(clientid)
			} else if msgtype == proto.MT_LOAD_ENTITY_ANYWHERE {
				eid := pkt.ReadEntityID()
				typeName := pkt.ReadVarStr()
				gs.HandleLoadEntityAnywhere(typeName, eid)
			} else if msgtype == proto.MT_CREATE_ENTITY_ANYWHERE {
				typeName := pkt.ReadVarStr()
				var data map[string]interface{}
				pkt.ReadData(&data)
				gs.HandleCreateEntityAnywhere(typeName, data)
			} else if msgtype == proto.MT_DECLARE_SERVICE {
				eid := pkt.ReadEntityID()
				serviceName := pkt.ReadVarStr()
				gs.HandleDeclareService(eid, serviceName)
			} else if msgtype == proto.MT_UNDECLARE_SERVICE {
				eid := pkt.ReadEntityID()
				serviceName := pkt.ReadVarStr()
				gs.HandleUndeclareService(eid, serviceName)
			} else if msgtype == proto.MT_NOTIFY_ALL_GAMES_CONNECTED {
				gs.HandleNotifyAllGamesConnected()
			} else if msgtype == proto.MT_NOTIFY_GATE_DISCONNECTED {
				gateid := pkt.ReadUint16()
				gs.HandleGateDisconnected(gateid)
			} else if msgtype == proto.MT_START_FREEZE_GAME_ACK {
				gs.HandleStartFreezeGameAck()
			} else {
				gwlog.TraceError("unknown msgtype: %v", msgtype)
				if consts.DEBUG_MODE {
					os.Exit(2)
				}
			}

			pkt.Release()
		case <-ticker:
			isTick = true
			runState := gs.runState.Load()
			if runState == rsTerminating {
				// game is terminating, run the terminating process
				gs.doTerminate()
			} else if runState == rsFreezing {
				//game is freezing, run freeze process
				gs.doFreeze()
			}

			timer.Tick()

			//case <-gs.collectEntitySyncInfosRequest: //
			//	gs.collectEntitySycnInfosReply <- 1
		}

		// after handling packets or firing timers, check the posted functions
		post.Tick()
		if isTick {
			gameDispatcherClientDelegate.HandleDispatcherClientBeforeFlush()
			dispatcher_client.GetDispatcherClientForSend().Flush()
		}
	}
}

func (gs *GameService) waitPostsComplete() {
	post.Tick() // just tick is Ok, tick will consume all posts
}

func (gs *GameService) doTerminate() {
	// wait for all posts to complete
{'fraction_non_alphanumeric': 0.1102533829806557, 'fraction_numerical': 0.001998001998001998, 'mean_word_length': 3.9425493716337523, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '792610', 'n_tokens_mistral': 4288, 'n_tokens_neox': 3838, 'n_words': 738}