@@ -28,6 +28,19 @@ const (
2828 syncConfigVersion uint16 = 1
2929 replayFormatVersion uint16 = 1
3030 replayMagic = "IKRPLCFG"
31+
32+ netLoadingPollTimeout = time .Millisecond
33+
34+ netLoadingReadyToken byte = 0xC7
35+ netLoadingAckToken byte = 0x7C
36+ )
37+
38+ type LoadingPhase uint8
39+
40+ const (
41+ LP_Idle LoadingPhase = iota
42+ LP_HostReadySent
43+ LP_GuestReadySent
3144)
3245
3346type NetState int
@@ -174,7 +187,7 @@ type NetConnection struct {
174187 closeOnce sync.Once
175188 uiInputDebounced bool
176189 headerWritten bool
177- loadingPhase uint8 // 0=idle, 1=host sent token
190+ loadingPhase LoadingPhase
178191}
179192
180193func NewNetConnection () * NetConnection {
@@ -211,7 +224,7 @@ func (nc *NetConnection) Close() {
211224 return
212225 }
213226 // Reset any pending loading rendezvous.
214- nc .loadingPhase = 0
227+ nc .loadingPhase = LP_Idle
215228 // Ensure connect/accept goroutines stop retrying/handshaking.
216229 nc .closeOnce .Do (func () {
217230 if nc .closing != nil {
@@ -438,6 +451,11 @@ func (nc *NetConnection) writeI8(i8 int8) error {
438451 return nil
439452}
440453
454+ func (nc * NetConnection ) writeU8 (u8 byte ) error {
455+ _ , err := nc .conn .Write ([]byte {u8 })
456+ return err
457+ }
458+
441459func (nc * NetConnection ) readI16 () (int16 , error ) {
442460 b := [2 ]byte {}
443461 if _ , err := nc .conn .Read (b [:]); err != nil {
@@ -536,7 +554,7 @@ func (nc *NetConnection) Synchronize() error {
536554 return Error ("Cannot connect to the other player" )
537555 }
538556 // Reset any pending loading rendezvous so it can't interfere with gameplay sync.
539- nc .loadingPhase = 0
557+ nc .loadingPhase = LP_Idle
540558 nc .Stop ()
541559
542560 header , err := sys .synchronizeNetplayConfig (nc )
@@ -697,85 +715,118 @@ func (nc *NetConnection) ResetLoadingPhase() {
697715 if nc == nil {
698716 return
699717 }
700- nc .loadingPhase = 0
718+ nc .loadingPhase = LP_Idle
701719}
702720
703721func (nc * NetConnection ) tryReadU8 () (byte , bool , error ) {
704722 if nc == nil || nc .conn == nil {
705723 return 0 , false , Error ("Cannot connect to the other player" )
706724 }
707725 b := [1 ]byte {}
708- _ = nc .conn .SetReadDeadline (time .Now ())
726+ _ = nc .conn .SetReadDeadline (time .Now (). Add ( netLoadingPollTimeout ) )
709727 n , err := nc .conn .Read (b [:])
710728 _ = nc .conn .SetReadDeadline (time.Time {})
729+ if n > 0 {
730+ return b [0 ], true , nil
731+ }
711732 if err != nil {
712733 if ne , ok := err .(net.Error ); ok && ne .Timeout () {
713734 return 0 , false , nil
714735 }
715736 return 0 , false , err
716737 }
717- if n <= 0 {
718- return 0 , false , nil
719- }
720- return b [0 ], true , nil
738+ return 0 , false , nil
739+ }
740+
741+ func (nc * NetConnection ) finishLoadingBarrier () {
742+ nc .loadingPhase = LP_Idle
743+ nc .time = 0
744+ nc .stoppedcnt = 0
721745}
722746
723747// Handshake for both peers finished loading
724748func (nc * NetConnection ) LoadingReady () (bool , error ) {
749+ if sys .esc || sys .gameEnd {
750+ return false , nil
751+ }
725752 if nc == nil || ! nc .IsConnected () || nc .st == NS_Error {
726753 return false , Error ("Cannot connect to the other player" )
727754 }
728755 nc .Stop ()
729756
730- const token byte = 0xC7
731- const tokenI8 int8 = - 57
732-
733757 // Host
734758 if nc .host {
735- // Phase 0: send token once.
736- if nc . loadingPhase == 0 {
737- if err := nc .writeI8 ( tokenI8 ); err != nil {
738- nc .loadingPhase = 0
759+ switch nc . loadingPhase {
760+ case LP_Idle :
761+ if err := nc .writeU8 ( netLoadingReadyToken ); err != nil {
762+ nc .loadingPhase = LP_Idle
739763 return false , err
740764 }
741- nc .loadingPhase = 1
765+ nc .loadingPhase = LP_HostReadySent
742766 return false , nil
767+ case LP_HostReadySent :
768+ v , ok , err := nc .tryReadU8 ()
769+ if err != nil {
770+ nc .loadingPhase = LP_Idle
771+ return false , err
772+ }
773+ if ! ok {
774+ return false , nil
775+ }
776+ if v != netLoadingReadyToken {
777+ nc .loadingPhase = LP_Idle
778+ return false , Error ("Synchronization error" )
779+ }
780+ if err := nc .writeU8 (netLoadingAckToken ); err != nil {
781+ nc .loadingPhase = LP_Idle
782+ return false , err
783+ }
784+ nc .finishLoadingBarrier ()
785+ return true , nil
743786 }
744- // Phase 1: wait for ack (non-blocking poll).
787+ nc .loadingPhase = LP_Idle
788+ return false , Error ("Synchronization error" )
789+ }
790+
791+ // Guest waits for host readiness, replies with its own readiness, then waits for the host ACK
792+ switch nc .loadingPhase {
793+ case LP_Idle :
745794 v , ok , err := nc .tryReadU8 ()
746795 if err != nil {
747- nc .loadingPhase = 0
796+ nc .loadingPhase = LP_Idle
748797 return false , err
749798 }
750799 if ! ok {
751800 return false , nil
752801 }
753- if v != token {
754- nc .loadingPhase = 0
802+ if v != netLoadingReadyToken {
803+ nc .loadingPhase = LP_Idle
755804 return false , Error ("Synchronization error" )
756805 }
757- nc .loadingPhase = 0
758- return true , nil
759- }
760-
761- // Guest: wait for token, then immediately ack.
762- v , ok , err := nc .tryReadU8 ()
763- if err != nil {
764- nc .loadingPhase = 0
765- return false , err
766- }
767- if ! ok {
806+ if err := nc .writeU8 (netLoadingReadyToken ); err != nil {
807+ nc .loadingPhase = LP_Idle
808+ return false , err
809+ }
810+ nc .loadingPhase = LP_GuestReadySent
768811 return false , nil
812+ case LP_GuestReadySent :
813+ v , ok , err := nc .tryReadU8 ()
814+ if err != nil {
815+ nc .loadingPhase = LP_Idle
816+ return false , err
817+ }
818+ if ! ok {
819+ return false , nil
820+ }
821+ if v != netLoadingAckToken {
822+ nc .loadingPhase = LP_Idle
823+ return false , Error ("Synchronization error" )
824+ }
825+ nc .finishLoadingBarrier ()
826+ return true , nil
769827 }
770- if v != token {
771- nc .loadingPhase = 0
772- return false , Error ("Synchronization error" )
773- }
774- if err := nc .writeI8 (tokenI8 ); err != nil {
775- nc .loadingPhase = 0
776- return false , err
777- }
778- return true , nil
828+ nc .loadingPhase = LP_Idle
829+ return false , Error ("Synchronization error" )
779830}
780831
781832func (nc * NetConnection ) Update () bool {
0 commit comments