diff --git a/.github/workflows/releaser.yml b/.github/workflows/releaser.yml index 1564a5d..313fadc 100644 --- a/.github/workflows/releaser.yml +++ b/.github/workflows/releaser.yml @@ -24,7 +24,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: '~1.22.0' + go-version: '~1.23.0' cache-dependency-path: | common/go.sum server/go.sum diff --git a/.goreleaser.yaml b/.goreleaser.yaml index a941af3..fdff830 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -6,8 +6,7 @@ builds: main: ./server binary: server goos: [ windows ] - goarch: [ "386", arm ] - goarm: [ "7" ] + goarch: [ "386" ] - id: server_32_linux main: ./server binary: server @@ -63,9 +62,8 @@ builds: main: ./server-genCert binary: bin/genCert goos: [ windows ] - goarch: [ "386", arm ] + goarch: [ "386" ] goamd64: [ "v1" ] - goarm: [ "7" ] - id: server-genCert_32_linux main: ./server-genCert binary: bin/genCert @@ -474,7 +472,6 @@ archives: {{- .RawVersion }}_win {{- if eq .Arch "386" }}_x86-32 {{- else if eq .Arch "amd64" }}_x86-64 - {{- else if eq .Arch "arm" }}_arm32 {{- else if eq .Arch "arm64" }}_arm64 {{- end }} files: diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 1e3b69a..2bdf104 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -5,7 +5,7 @@ Copy `go.work.example` to `go.work` ### System requirements - OS requirements correspond to the server/launcher ones. Cross-compilation works on all out-the-box. -- [Go 1.22](https://go.dev/dl/). +- [Go 1.23](https://go.dev/dl/). - [Git](https://git-scm.com/downloads). - [Task](https://taskfile.dev/installation/). - [GoReleaser](https://goreleaser.com/). diff --git a/README.md b/README.md index aacaa9f..50799c1 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ Supported games: connected to the internet and Steam or Xbox Live, depending on the platform and version, to fully play offline.** -⚠️ **Repository is now on maintenance only due to lack of contributions/funding and having completed the main goals. Expect only updates to fix bugs and for dependencies/compiler.** +⚠️ **Repository is now on maintenance only due to lack of contributions/funding and having completed the main goals. +Expect only updates to fix bugs and for dependencies/compiler.** ℹ️ My other [project](https://github.com/luskaner/ageLANServerLauncherCompanion) provides the files and information to download a Steam Emulator and play 100% offline. @@ -113,11 +114,13 @@ in [Questions and Answers (QA)](https://github.com/luskaner/ageLANServer/wiki/Qu #### Stable -- Windows 10 (no S edition/mode). -- Windows Server 2016. -- Windows IoT. +- Windows: + - 10 (no S edition/mode). + - (Storage) Server 2016. + - IoT (no Arm32). + - Server IoT 2019. - Linux: kernel 2.6.32 (see [here](https://go.dev/wiki/Linux) for more details). -- macOS: Catalina (v10.15). +- macOS: Big Sur (v11). Admin rights or firewall permission to listen on port 443 (https) will likely be required depending on the operating system. @@ -129,14 +132,14 @@ system. - Solaris-based (Solaris and Illumos). - AIX. -Note: For the full list see [minimum requirements for Go](https://go.dev/wiki/MinimumRequirements) 1.22. +Note: For the full list see [minimum requirements for Go](https://go.dev/wiki/MinimumRequirements) 1.23. ### Launcher - Windows (no S edition/mode): - - 10 (1803 - Redstone 5) on x86-64 (recommended). + - 10 on x86-64 (recommended). - 11 on ARM. - Linux: *recent* distribution with Steam on x86-64 using Steam Play. @@ -184,9 +187,8 @@ supported operating systems. * **ARM64**: ...\_launcher\_*A.B.C*_linux_arm64.tar.xz * Server: * Windows: - * **10, Server 2025 or IoT on ARM64**: ...\_server\_*A.B.C*_win_arm64.zip - * **10 IoT on ARM32**: ...\_server\_*A.B.C*_win_arm32.zip - * **10, Server 2016 or IoT on x86-64**: ...\_server\_*A.B.C*_win_x86-64.zip + * **10, Server (IoT) 2025 or IoT on ARM64**: ...\_server\_*A.B.C*_win_arm64.zip + * **10, (Storage) Server 2016, Server IoT 2019 or IoT on x86-64**: ...\_server\_*A.B.C*_win_x86-64.zip * **10 or 10 IoT on x86-32**: ...\_server\_*A.B.C*_win_x86-32.zip * Linux: * Kernel 3.1 on **ARM64**: ...\_server\_*A.B.C*_linux_arm64.tar.xz diff --git a/common/cert.go b/common/cert.go index 6c40cf6..d148d10 100644 --- a/common/cert.go +++ b/common/cert.go @@ -22,16 +22,18 @@ func CertificatePairFolder(executablePath string) string { return folder } -func HasCertificatePair(executablePath string) bool { - parentDir := CertificatePairFolder(executablePath) +func CertificatePair(executablePath string) (ok bool, parentDir string, cert string) { + parentDir = CertificatePairFolder(executablePath) if parentDir == "" { - return false + return } - if _, err := os.Stat(filepath.Join(parentDir, Cert)); os.IsNotExist(err) { - return false + cert = filepath.Join(parentDir, Cert) + if _, err := os.Stat(cert); os.IsNotExist(err) { + return } if _, err := os.Stat(filepath.Join(parentDir, Key)); os.IsNotExist(err) { - return false + return } - return true + ok = true + return } diff --git a/common/game.go b/common/game.go index d1c8d83..a646909 100644 --- a/common/game.go +++ b/common/game.go @@ -8,4 +8,4 @@ const ( GameAoE3 = "age3" ) -var SupportedGames = mapset.NewSet[string](GameAoE1, GameAoE2, GameAoE3) +var SupportedGames = mapset.NewThreadUnsafeSet[string](GameAoE1, GameAoE2, GameAoE3) diff --git a/common/go.mod b/common/go.mod index 2bb36ab..e5e9171 100644 --- a/common/go.mod +++ b/common/go.mod @@ -1,6 +1,6 @@ module github.com/luskaner/ageLANServer/common -go 1.22.0 +go 1.23.0 require ( github.com/deckarep/golang-set/v2 v2.7.0 diff --git a/common/process/process.go b/common/process/process.go index bcf1ccd..ea63393 100644 --- a/common/process/process.go +++ b/common/process/process.go @@ -103,7 +103,7 @@ func Kill(exe string) (proc *os.Process, err error) { } func GameProcesses(gameId string, steam bool, xbox bool) []string { - processes := mapset.NewSet[string]() + processes := mapset.NewThreadUnsafeSet[string]() if steam { processes.Add(steamProcess(gameId)) } diff --git a/common/process/process_unix.go b/common/process/process_unix.go index e56cb4c..c027707 100644 --- a/common/process/process_unix.go +++ b/common/process/process_unix.go @@ -7,6 +7,7 @@ import ( "mvdan.cc/sh/v3/shell" "os" "path/filepath" + "slices" "strconv" ) @@ -32,10 +33,8 @@ func ProcessesPID(names []string) map[string]uint32 { continue } cmdlineName := filepath.Base(args[0]) - for _, name := range names { - if cmdlineName == name { - processesPid[name] = uint32(pid) - } + if slices.Contains(names, cmdlineName) { + processesPid[cmdlineName] = uint32(pid) } } } diff --git a/common/process/process_windows.go b/common/process/process_windows.go index eb7ad7b..59929b9 100644 --- a/common/process/process_windows.go +++ b/common/process/process_windows.go @@ -3,6 +3,7 @@ package process import ( "golang.org/x/sys/windows" "os" + "slices" "unsafe" ) @@ -11,12 +12,7 @@ func ProcessesPID(names []string) map[string]uint32 { return windows.UTF16ToString(entry.ExeFile[:]) } entries := processesEntry(func(entry *windows.ProcessEntry32) bool { - for _, n := range names { - if name(entry) == n { - return true - } - } - return false + return slices.Contains(names, name(entry)) }, false) processes := make(map[string]uint32) for _, entry := range entries { diff --git a/go.work.example b/go.work.example index bedd8c8..92739d3 100644 --- a/go.work.example +++ b/go.work.example @@ -1,4 +1,4 @@ -go 1.22.0 +go 1.23.0 use ( battle-server-broadcast diff --git a/launcher-agent/go.mod b/launcher-agent/go.mod index 74e3e69..220da83 100644 --- a/launcher-agent/go.mod +++ b/launcher-agent/go.mod @@ -1,6 +1,6 @@ module github.com/luskaner/ageLANServer/launcher-agent -go 1.22.0 +go 1.23.0 require ( github.com/luskaner/ageLANServer/battle-server-broadcast v1.3.0 diff --git a/launcher-common/go.mod b/launcher-common/go.mod index e8b6242..167e4c7 100644 --- a/launcher-common/go.mod +++ b/launcher-common/go.mod @@ -1,6 +1,6 @@ module github.com/luskaner/ageLANServer/launcher-common -go 1.22.0 +go 1.23.0 require ( github.com/andygrunwald/vdf v1.1.0 diff --git a/launcher-common/ipc.go b/launcher-common/ipc.go index 6a05cb2..2d8abea 100644 --- a/launcher-common/ipc.go +++ b/launcher-common/ipc.go @@ -3,13 +3,12 @@ package launcher_common import ( "github.com/luskaner/ageLANServer/common" "net" - "os" - "path" ) const ConfigAdminIpcRevert byte = 0 const ConfigAdminIpcSetup byte = 1 const ConfigAdminIpcExit byte = 2 +const configAdminIpcName = common.Name + `-launcher-config-admin-agent` type ( ConfigAdminIpcSetupCommand struct { @@ -23,7 +22,3 @@ type ( Certificate bool } ) - -func ConfigAdminIpcName() string { - return path.Join(os.TempDir(), common.Name+`-launcher-config-admin-agent`) -} diff --git a/launcher-common/ipc_other.go b/launcher-common/ipc_other.go new file mode 100644 index 0000000..174f7e7 --- /dev/null +++ b/launcher-common/ipc_other.go @@ -0,0 +1,12 @@ +//go:build !windows + +package launcher_common + +import ( + "os" + "path" +) + +func ConfigAdminIpcPath() string { + return path.Join(os.TempDir(), configAdminIpcName) +} diff --git a/launcher-common/ipc_windows.go b/launcher-common/ipc_windows.go new file mode 100644 index 0000000..f467969 --- /dev/null +++ b/launcher-common/ipc_windows.go @@ -0,0 +1,5 @@ +package launcher_common + +func ConfigAdminIpcPath() string { + return `\\.\pipe\` + configAdminIpcName +} diff --git a/launcher-common/resolve.go b/launcher-common/resolve.go index 4e1aafe..b426f03 100644 --- a/launcher-common/resolve.go +++ b/launcher-common/resolve.go @@ -50,11 +50,11 @@ func cachedIpToHosts(ip string) (bool, mapset.Set[string]) { func cacheMapping(host string, ip string) { hostToLower := strings.ToLower(host) if _, exists := hostToIps[hostToLower]; !exists { - hostToIps[hostToLower] = mapset.NewSet[string]() + hostToIps[hostToLower] = mapset.NewThreadUnsafeSet[string]() } hostToIps[hostToLower].Add(ip) if _, exists := ipToHosts[ip]; !exists { - ipToHosts[ip] = mapset.NewSet[string]() + ipToHosts[ip] = mapset.NewThreadUnsafeSet[string]() } ipToHosts[ip].Add(host) if _, exists := failedIpToHosts[ip]; exists { @@ -67,7 +67,7 @@ func cacheMapping(host string, ip string) { func HostOrIpToIps(host string) mapset.Set[string] { if ip := net.ParseIP(host); ip != nil { - var ips = mapset.NewSet[string]() + var ips = mapset.NewThreadUnsafeSet[string]() if ip.To4() != nil { if ip.IsUnspecified() { ips.Append(ResolveUnspecifiedIps()...) @@ -81,7 +81,7 @@ func HostOrIpToIps(host string) mapset.Set[string] { if cached { return cachedIps } - ips := mapset.NewSet[string]() + ips := mapset.NewThreadUnsafeSet[string]() ipsFromDns := common.HostToIps(host) if ipsFromDns != nil { for _, ipRaw := range ipsFromDns { @@ -144,7 +144,7 @@ func IpToHosts(ip string) mapset.Set[string] { if cached { return cachedHosts } - hosts := mapset.NewSet[string]() + hosts := mapset.NewThreadUnsafeSet[string]() hostsFromDns := ipToDnsName(ip) if hostsFromDns != nil { for _, hostStr := range hostsFromDns { diff --git a/launcher-config-admin-agent/go.mod b/launcher-config-admin-agent/go.mod index eca646c..85a9a7e 100644 --- a/launcher-config-admin-agent/go.mod +++ b/launcher-config-admin-agent/go.mod @@ -1,6 +1,6 @@ module github.com/luskaner/ageLANServer/launcher-config-admin-agent -go 1.22.0 +go 1.23.0 require ( github.com/luskaner/ageLANServer/common v0.0.0-20241204232812-4e96c2e19aea diff --git a/launcher-config-admin-agent/internal/ipc.go b/launcher-config-admin-agent/internal/ipc.go index c26e408..0411881 100644 --- a/launcher-config-admin-agent/internal/ipc.go +++ b/launcher-config-admin-agent/internal/ipc.go @@ -7,7 +7,6 @@ import ( launcherCommon "github.com/luskaner/ageLANServer/launcher-common" "github.com/luskaner/ageLANServer/launcher-common/executor" "net" - "os" ) var mappedCdn = false @@ -121,30 +120,16 @@ func handleRevert(decoder *gob.Decoder) int { } func RunIpcServer() (errorCode int) { - ipcPath := launcherCommon.ConfigAdminIpcName() - - if err := os.Remove(ipcPath); err != nil && !os.IsNotExist(err) { - errorCode = ErrListen - return - } - - defer func() { - _ = os.Remove(ipcPath) - }() - - l, err := net.Listen("unix", ipcPath) + l, err := SetupIpcServer() if err != nil { errorCode = ErrListen return } defer func(l net.Listener) { _ = l.Close() + RevertIpcServer() }(l) - if err = os.Chmod(ipcPath, 0666); err != nil { - return - } - var conn net.Conn for { conn, err = l.Accept() diff --git a/launcher-config-admin-agent/internal/ipc_other.go b/launcher-config-admin-agent/internal/ipc_other.go new file mode 100644 index 0000000..78e5696 --- /dev/null +++ b/launcher-config-admin-agent/internal/ipc_other.go @@ -0,0 +1,30 @@ +//go:build !windows + +package internal + +import ( + launcherCommon "github.com/luskaner/ageLANServer/launcher-common" + "net" + "os" +) + +func SetupIpcServer() (listener net.Listener, err error) { + ipcPath := launcherCommon.ConfigAdminIpcPath() + + if err = os.Remove(ipcPath); err != nil && !os.IsNotExist(err) { + return + } + + listener, err = net.Listen("unix", ipcPath) + + if err != nil { + return + } + + err = os.Chmod(ipcPath, 0666) + return +} + +func RevertIpcServer() { + _ = os.Remove(launcherCommon.ConfigAdminIpcPath()) +} diff --git a/launcher-config-admin-agent/internal/ipc_windows.go b/launcher-config-admin-agent/internal/ipc_windows.go new file mode 100644 index 0000000..c6c6551 --- /dev/null +++ b/launcher-config-admin-agent/internal/ipc_windows.go @@ -0,0 +1,26 @@ +package internal + +import ( + "fmt" + "github.com/Microsoft/go-winio" + launcherCommon "github.com/luskaner/ageLANServer/launcher-common" + "net" + "os/user" +) + +func SetupIpcServer() (listener net.Listener, err error) { + var u *user.User + u, err = user.Current() + if err != nil { + return nil, err + } + pc := &winio.PipeConfig{ + InputBufferSize: 1_024, + OutputBufferSize: 1, + SecurityDescriptor: fmt.Sprintf("D:P(A;;GA;;;%s)", u.Uid), + MessageMode: true, + } + return winio.ListenPipe(launcherCommon.ConfigAdminIpcPath(), pc) +} + +func RevertIpcServer() {} diff --git a/launcher-config-admin/go.mod b/launcher-config-admin/go.mod index 80f32bf..bd297d1 100644 --- a/launcher-config-admin/go.mod +++ b/launcher-config-admin/go.mod @@ -1,6 +1,6 @@ module github.com/luskaner/ageLANServer/launcher-config-admin -go 1.22.0 +go 1.23.0 require ( github.com/deckarep/golang-set/v2 v2.7.0 diff --git a/launcher-config-admin/internal/cmd/revert.go b/launcher-config-admin/internal/cmd/revert.go index 3750f95..8a34362 100644 --- a/launcher-config-admin/internal/cmd/revert.go +++ b/launcher-config-admin/internal/cmd/revert.go @@ -58,7 +58,7 @@ var revertCmd = &cobra.Command{ } } if cmd.UnmapCDN || cmd.UnmapIPs { - hsts := mapset.NewSet[string]() + hsts := mapset.NewThreadUnsafeSet[string]() if cmd.UnmapIPs { hsts.Add(common.Domain) } diff --git a/launcher-config-admin/internal/cmd/setUp.go b/launcher-config-admin/internal/cmd/setUp.go index a26c5bf..dc784b8 100644 --- a/launcher-config-admin/internal/cmd/setUp.go +++ b/launcher-config-admin/internal/cmd/setUp.go @@ -64,13 +64,13 @@ var setUpCmd = &cobra.Command{ fmt.Println("Adding IP mappings") mappings := make(map[string]mapset.Set[string]) if len(cmd.MapIPs) > 0 { - mappings[common.Domain] = mapset.NewSet[string]() + mappings[common.Domain] = mapset.NewThreadUnsafeSet[string]() for _, ip := range cmd.MapIPs { mappings[common.Domain].Add(ip.String()) } } if cmd.MapCDN { - mappings[launcherCommon.CDNDomain] = mapset.NewSet[string]() + mappings[launcherCommon.CDNDomain] = mapset.NewThreadUnsafeSet[string]() mappings[launcherCommon.CDNDomain].Add(launcherCommon.CDNIP) } if ok, _ := hosts.AddHosts(mappings); ok { diff --git a/launcher-config-admin/internal/hosts/hosts.go b/launcher-config-admin/internal/hosts/hosts.go index 1eca211..9c800a0 100644 --- a/launcher-config-admin/internal/hosts/hosts.go +++ b/launcher-config-admin/internal/hosts/hosts.go @@ -59,7 +59,7 @@ func getExistingHosts(hosts mapset.Set[string]) (err error, existingHosts mapset if f == nil { return } - existingHosts = mapset.NewSet[string]() + existingHosts = mapset.NewThreadUnsafeSet[string]() var line string scanner := bufio.NewScanner(f) for scanner.Scan() { diff --git a/launcher-config/go.mod b/launcher-config/go.mod index 86d773a..272315a 100644 --- a/launcher-config/go.mod +++ b/launcher-config/go.mod @@ -1,8 +1,9 @@ module github.com/luskaner/ageLANServer/launcher-config -go 1.22.0 +go 1.23.0 require ( + github.com/Microsoft/go-winio v0.6.2 github.com/deckarep/golang-set/v2 v2.7.0 github.com/luskaner/ageLANServer/common v0.0.0-20241204232812-4e96c2e19aea github.com/luskaner/ageLANServer/launcher-common v0.0.0-20241225211103-3a8661980cd5 diff --git a/launcher-config/go.sum b/launcher-config/go.sum index ddf5415..4849d7e 100644 --- a/launcher-config/go.sum +++ b/launcher-config/go.sum @@ -1,3 +1,5 @@ +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/andygrunwald/vdf v1.1.0 h1:gmstp0R7DOepIZvWoSJY97ix7QOrsxpGPU6KusKXqvw= github.com/andygrunwald/vdf v1.1.0/go.mod h1:f31AAs7HOKvs5B167iwLHwKuqKc4bE46Vdt7xQogA0o= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= diff --git a/launcher-config/internal/admin.go b/launcher-config/internal/admin.go index 4e63b9e..057e4ef 100644 --- a/launcher-config/internal/admin.go +++ b/launcher-config/internal/admin.go @@ -93,7 +93,7 @@ func ConnectAgentIfNeeded() (err error) { return } var conn net.Conn - conn, err = net.Dial("unix", launcherCommon.ConfigAdminIpcName()) + conn, err = DialIPC() if err != nil { return } diff --git a/launcher-config/internal/admin_other.go b/launcher-config/internal/admin_other.go index 0186fd6..22d5012 100644 --- a/launcher-config/internal/admin_other.go +++ b/launcher-config/internal/admin_other.go @@ -6,6 +6,8 @@ import ( "fmt" "github.com/luskaner/ageLANServer/common/executor" "github.com/luskaner/ageLANServer/common/process" + launcherCommon "github.com/luskaner/ageLANServer/launcher-common" + "net" "time" ) @@ -26,3 +28,7 @@ func postAgentStart(file string) { } } } + +func DialIPC() (net.Conn, error) { + return net.Dial("unix", launcherCommon.ConfigAdminIpcPath()) +} diff --git a/launcher-config/internal/admin_windows.go b/launcher-config/internal/admin_windows.go index 3e9857b..287401f 100644 --- a/launcher-config/internal/admin_windows.go +++ b/launcher-config/internal/admin_windows.go @@ -1,4 +1,14 @@ package internal +import ( + "github.com/Microsoft/go-winio" + launcherCommon "github.com/luskaner/ageLANServer/launcher-common" + "net" +) + func preAgentStart() {} func postAgentStart(_ string) {} + +func DialIPC() (net.Conn, error) { + return winio.DialPipe(launcherCommon.ConfigAdminIpcPath(), nil) +} diff --git a/launcher-config/internal/cmd/setUp.go b/launcher-config/internal/cmd/setUp.go index 76783cd..e782b7f 100644 --- a/launcher-config/internal/cmd/setUp.go +++ b/launcher-config/internal/cmd/setUp.go @@ -154,7 +154,7 @@ var setUpCmd = &cobra.Command{ os.Exit(errorCode) } } - hostMappings := mapset.NewSet[string]() + hostMappings := mapset.NewThreadUnsafeSet[string]() if len(cmd.MapIPs) > 0 { for _, ip := range cmd.MapIPs { hostMappings.Add(ip.String()) diff --git a/launcher/README.md b/launcher/README.md index 44b4d34..5fb1613 100644 --- a/launcher/README.md +++ b/launcher/README.md @@ -8,7 +8,7 @@ system and reverting that configuration upon exit. ## Minimum system Requirements - Windows (no S edition/mode): - - 10 (1803 - Redstone 5) on x86-64 (recommended). + - 10 on x86-64 (recommended). - 11 on ARM. - Linux: *recent* distribution with Steam on x86-64 using Steam Play. diff --git a/launcher/go.mod b/launcher/go.mod index b698439..5b4c722 100644 --- a/launcher/go.mod +++ b/launcher/go.mod @@ -1,6 +1,6 @@ module github.com/luskaner/ageLANServer/launcher -go 1.22.0 +go 1.23.0 require ( github.com/deckarep/golang-set/v2 v2.7.0 diff --git a/launcher/internal/cmd/root.go b/launcher/internal/cmd/root.go index 4e6528b..618a285 100644 --- a/launcher/internal/cmd/root.go +++ b/launcher/internal/cmd/root.go @@ -35,14 +35,14 @@ var configPaths = []string{"resources", "."} var config = &cmdUtils.Config{} func parseCommandArgs(name string, values map[string]string) (args []string, err error) { - cmd := strings.Join(viper.GetStringSlice(name), " ") + cmdArgs := strings.Join(viper.GetStringSlice(name), " ") for key, value := range values { - cmd = strings.ReplaceAll(cmd, fmt.Sprintf(`{%s}`, key), value) + cmdArgs = strings.ReplaceAll(cmdArgs, fmt.Sprintf(`{%s}`, key), value) } if runtime.GOOS == "windows" { - cmd = reWinToLinVar.ReplaceAllString(cmd, `$$$1`) + cmdArgs = reWinToLinVar.ReplaceAllString(cmdArgs, `$$$1`) } - args, err = shell.Fields(cmd, nil) + args, err = shell.Fields(cmdArgs, nil) return } @@ -50,9 +50,9 @@ var ( Version string cfgFile string gameCfgFile string - autoTrueFalseValues = mapset.NewSet[string](autoValue, trueValue, falseValue) - canTrustCertificateValues = mapset.NewSet[string](falseValue, "user", "local") - canBroadcastBattleServerValues = mapset.NewSet[string](autoValue, falseValue) + autoTrueFalseValues = mapset.NewThreadUnsafeSet[string](autoValue, trueValue, falseValue) + canTrustCertificateValues = mapset.NewThreadUnsafeSet[string](falseValue, "user", "local") + canBroadcastBattleServerValues = mapset.NewThreadUnsafeSet[string](autoValue, falseValue) rootCmd = &cobra.Command{ Use: filepath.Base(os.Args[0]), Short: "launcher discovers and configures AoE 1, AoE 2 and AoE 3 (all DE) to connect to the local LAN server", @@ -70,11 +70,6 @@ var ( _ = lock.Unlock() os.Exit(errorCode) }() - if err := cmdUtils.CheckVersion(); err != nil { - fmt.Println(err) - errorCode = internal.ErrUnsupportedOS - return - } canTrustCertificate := viper.GetString("Config.CanTrustCertificate") if runtime.GOOS != "windows" { canTrustCertificateValues.Remove("user") diff --git a/launcher/internal/cmdUtils/hosts.go b/launcher/internal/cmdUtils/hosts.go index 4fde4e5..80eda1a 100644 --- a/launcher/internal/cmdUtils/hosts.go +++ b/launcher/internal/cmdUtils/hosts.go @@ -69,7 +69,7 @@ func requiresMapCDN() bool { func (c *Config) MapHosts(host string, canMap bool, alreadySelectedIp bool) (errorCode int) { var mapCDN bool - ips := mapset.NewSet[string]() + ips := mapset.NewThreadUnsafeSet[string]() if requiresMapCDN() { if !canMap { fmt.Println("canAddHost is false but CDN is required to be mapped. You should have added the", launcherCommon.CDNIP, "mapping to", launcherCommon.CDNDomain, "in the hosts file (or just set canAddHost to true).") diff --git a/launcher/internal/cmdUtils/server.go b/launcher/internal/cmdUtils/server.go index dd284e9..987cda8 100644 --- a/launcher/internal/cmdUtils/server.go +++ b/launcher/internal/cmdUtils/server.go @@ -79,14 +79,14 @@ func ListenToServerAnnouncementsAndSelectBestIp(gameId string, multicastIPs []ne for _, data := range servers { if data.Version >= common.AnnounceVersion1 { announceData := data.Data.(common.AnnounceMessageData001) - gameIdSet := mapset.NewSet[string](announceData.GameIds...) + gameIdSet := mapset.NewThreadUnsafeSet[string](announceData.GameIds...) if !gameIdSet.ContainsOne(gameId) { continue } } ips := data.Ips.ToSlice() sort.Strings(ips) - hosts := mapset.NewSet[string]() + hosts := mapset.NewThreadUnsafeSet[string]() for _, ip := range ips { hosts.Append(launcherCommon.IpToHosts(ip).ToSlice()...) } @@ -170,13 +170,13 @@ func (c *Config) StartServer(executable string, args []string, stop bool, canTru if executable != serverExecutablePath { fmt.Println("Found server executable path:", serverExecutablePath) } - if !common.HasCertificatePair(serverExecutablePath) { + + if exists, certificateFolder, cert := common.CertificatePair(serverExecutablePath); !exists || server.CertificateSoonExpired(cert) { if !canTrustCertificate { - fmt.Println("serverStart is true and canTrustCertificate is false. Certificate pair is missing. Generate your own certificates manually.") - errorCode = internal.ErrServerCertMissing + fmt.Println("serverStart is true and canTrustCertificate is false. Certificate pair is missing or soon expired. Generate your own certificates manually.") + errorCode = internal.ErrServerCertMissingExpired return } - certificateFolder := common.CertificatePairFolder(serverExecutablePath) if certificateFolder == "" { fmt.Println("Cannot find certificate folder of the server. Make sure the folder structure of the server is correct.") errorCode = internal.ErrServerCertDirectory diff --git a/launcher/internal/cmdUtils/version_other.go b/launcher/internal/cmdUtils/version_other.go deleted file mode 100644 index 6a25825..0000000 --- a/launcher/internal/cmdUtils/version_other.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build !windows - -package cmdUtils - -func CheckVersion() error { - return nil -} diff --git a/launcher/internal/cmdUtils/version_windows.go b/launcher/internal/cmdUtils/version_windows.go deleted file mode 100644 index 35a77d8..0000000 --- a/launcher/internal/cmdUtils/version_windows.go +++ /dev/null @@ -1,34 +0,0 @@ -package cmdUtils - -import ( - "fmt" - "golang.org/x/sys/windows" -) - -const minMajorVersion = 10 -const minMinorVersion = 0 -const minBuildNumber = 17763 -const alias = "Windows 10 (1809 - Redstone 5)" - -func CheckVersion() error { - major, minor, build := windows.RtlGetNtVersionNumbers() - if major > minMajorVersion { - return nil - } - if major == minMajorVersion && minor > minMinorVersion { - return nil - } - if major == minMajorVersion && minor == minMinorVersion && build >= minBuildNumber { - return nil - } - return fmt.Errorf( - "unsupported Windows version: %d.%d.%d, minimum is %d.%d.%d also known as %s. Update your system", - major, - minor, - build, - minMajorVersion, - minMinorVersion, - minBuildNumber, - alias, - ) -} diff --git a/launcher/internal/errors.go b/launcher/internal/errors.go index cfa54fd..23c286d 100644 --- a/launcher/internal/errors.go +++ b/launcher/internal/errors.go @@ -13,12 +13,11 @@ const ( ErrGameAlreadyRunning ErrGameLauncherNotFound ErrGameLauncherStart - ErrAnnouncementPort ErrListenServerAnnouncements ErrServerExecutable ErrServerConnectSecure ErrServerUnreachable - ErrServerCertMissing + ErrServerCertMissingExpired ErrServerCertDirectory ErrServerCertCreate ErrServerStart @@ -39,5 +38,4 @@ const ( ErrConfigCDNMap ErrSteamRoot ErrAnnouncementMulticastGroup - ErrUnsupportedOS ) diff --git a/launcher/internal/game/executor_windows.go b/launcher/internal/game/executor_windows.go index 2f631d3..45c715e 100644 --- a/launcher/internal/game/executor_windows.go +++ b/launcher/internal/game/executor_windows.go @@ -27,7 +27,7 @@ func appName(id string) string { } func isInstalledOnXbox(id string) bool { - // Does not seem there is another way without cgo? + // TODO: Implement natively return commonExecutor.Options{ File: "powershell", SpecialFile: true, diff --git a/launcher/internal/server/server.go b/launcher/internal/server/server.go index e3c4d00..69943db 100644 --- a/launcher/internal/server/server.go +++ b/launcher/internal/server/server.go @@ -215,7 +215,7 @@ func LanServersAnnounced(multicastIPs []net.IP, ports []int) map[uuid.UUID]*comm m = &common.AnnounceMessage{ Version: version, Data: data, - Ips: mapset.NewSet[string](), + Ips: mapset.NewThreadUnsafeSet[string](), } servers[id] = m } diff --git a/launcher/internal/server/ssl.go b/launcher/internal/server/ssl.go index b67336b..b6d8723 100644 --- a/launcher/internal/server/ssl.go +++ b/launcher/internal/server/ssl.go @@ -3,11 +3,13 @@ package server import ( "crypto/tls" "crypto/x509" + "encoding/pem" "github.com/luskaner/ageLANServer/common" "github.com/luskaner/ageLANServer/launcher-common/executor/exec" "net" "os" "path/filepath" + "time" ) func TlsConfig(insecureSkipVerify bool) *tls.Config { @@ -56,6 +58,29 @@ func GenerateCertificatePair(certificateFolder string) (result *exec.Result) { if _, err := os.Stat(exePath); err != nil { return nil } - result = exec.Options{File: exePath, Wait: true, ExitCode: true}.Exec() + result = exec.Options{File: exePath, Wait: true, Args: []string{"-r"}, ExitCode: true}.Exec() return } + +func CertificateSoonExpired(cert string) bool { + if cert == "" { + return true + } + + certPEM, err := os.ReadFile(cert) + if err != nil { + return true + } + + block, _ := pem.Decode(certPEM) + if block == nil { + return true + } + + crt, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return true + } + + return time.Now().Add(24 * time.Hour).After(crt.NotAfter) +} diff --git a/server-docker/genCert/Dockerfile b/server-docker/genCert/Dockerfile index d1bfc94..c7eb85a 100644 --- a/server-docker/genCert/Dockerfile +++ b/server-docker/genCert/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 # Compile -FROM golang:1.22-alpine3.20 AS compiler +FROM golang:1.23-alpine3.21 AS compiler WORKDIR /app COPY server-docker/genCert/go.work.template go.work COPY common common @@ -10,7 +10,7 @@ RUN mkdir -p build/resources/certificates RUN mkdir build/bin RUN go build -ldflags="-s -w" -o build/bin/genCert ./server-genCert # Compress -FROM alpine:3.20 AS compressor +FROM alpine:3.21 AS compressor RUN apk add --no-cache upx WORKDIR /app COPY --from=compiler /app/build . diff --git a/server-docker/genCert/go.work.template b/server-docker/genCert/go.work.template index ff3fa3a..916bacd 100644 --- a/server-docker/genCert/go.work.template +++ b/server-docker/genCert/go.work.template @@ -1,4 +1,4 @@ -go 1.22.0 +go 1.23.0 use ( common diff --git a/server-docker/server/Dockerfile b/server-docker/server/Dockerfile index 71117d8..caefb2f 100644 --- a/server-docker/server/Dockerfile +++ b/server-docker/server/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1 # Compile -FROM golang:1.22-alpine3.20 AS compiler +FROM golang:1.23-alpine3.21 AS compiler WORKDIR /app COPY server-docker/server/go.work.template go.work COPY common common @@ -11,7 +11,7 @@ RUN cp -r server/resources build/resources && rm -rf build/resources/windows && RUN mkdir -p build/resources/certificates RUN go build -ldflags="-s -w" -o build/server ./server # Compress -FROM alpine:3.20 AS compressor +FROM alpine:3.21 AS compressor RUN apk add --no-cache upx WORKDIR /app COPY --from=compiler /app/build/server . diff --git a/server-docker/server/go.work.template b/server-docker/server/go.work.template index 1fbc73c..4dddd75 100644 --- a/server-docker/server/go.work.template +++ b/server-docker/server/go.work.template @@ -1,4 +1,4 @@ -go 1.22.0 +go 1.23.0 use ( common diff --git a/server-genCert/go.mod b/server-genCert/go.mod index 6faad54..efb99d2 100644 --- a/server-genCert/go.mod +++ b/server-genCert/go.mod @@ -1,6 +1,6 @@ module github.com/luskaner/ageLANServer/server-genCert -go 1.22.0 +go 1.23.0 require github.com/spf13/cobra v1.8.1 diff --git a/server-genCert/internal/cmd/root.go b/server-genCert/internal/cmd/root.go index dc1cd1a..99b5b45 100644 --- a/server-genCert/internal/cmd/root.go +++ b/server-genCert/internal/cmd/root.go @@ -25,9 +25,11 @@ var ( fmt.Println("Failed to determine certificate pair folder") os.Exit(internal.ErrCertDirectory) } - if !replace && common.HasCertificatePair(serverExe) { - fmt.Println("Already have certificate pair and force is false, set force to true or delete it manually.") - os.Exit(internal.ErrCertCreateExisting) + if !replace { + if exists, _, _ := common.CertificatePair(serverExe); exists { + fmt.Println("Already have certificate pair and force is false, set force to true or delete it manually.") + os.Exit(internal.ErrCertCreateExisting) + } } if !internal.GenerateCertificatePair(serverFolder) { fmt.Println("Could not generate certificate pair.") diff --git a/server/README.md b/server/README.md index 2b87f39..d538c38 100644 --- a/server/README.md +++ b/server/README.md @@ -9,11 +9,13 @@ API requests. The server reimplements the minimum required API surface to allow #### Stable -- Windows 10 (no S edition/mode). -- Windows Server 2016. -- Windows IoT. +- Windows: + - 10 (no S edition/mode). + - (Storage) Server 2016. + - IoT (no Arm32). + - Server IoT 2019. - Linux: kernel 2.6.32 (see [here](https://go.dev/wiki/Linux) for more details). -- macOS: Catalina (v10.15). +- macOS: Big Sur (v11). Admin rights or firewall permission to listen on port 443 (https) will likely be required depending on the operating system. @@ -25,7 +27,7 @@ system. - Solaris-based (Solaris and Illumos). - AIX. -Note: For the full list see [minimum requirements for Go](https://go.dev/wiki/MinimumRequirements) 1.22. +Note: For the full list see [minimum requirements for Go](https://go.dev/wiki/MinimumRequirements) 1.23. diff --git a/server/go.mod b/server/go.mod index 90e4827..5f29a53 100644 --- a/server/go.mod +++ b/server/go.mod @@ -1,9 +1,10 @@ module github.com/luskaner/ageLANServer/server -go 1.22.0 +go 1.23.0 require ( github.com/deckarep/golang-set/v2 v2.7.0 + github.com/elliotchance/orderedmap/v3 v3.1.0 github.com/google/uuid v1.6.0 github.com/gorilla/handlers v1.5.2 github.com/gorilla/schema v1.4.1 @@ -11,19 +12,15 @@ require ( github.com/luskaner/ageLANServer/common v0.0.0-20241204232812-4e96c2e19aea github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 - github.com/wk8/go-ordered-map/v2 v2.1.8 golang.org/x/net v0.34.0 ) require ( - github.com/bahlo/generic-list-go v0.2.0 // indirect - github.com/buger/jsonparser v1.1.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/magiconair/properties v1.8.7 // indirect - github.com/mailru/easyjson v0.7.7 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect diff --git a/server/go.sum b/server/go.sum index 1f04903..e355007 100644 --- a/server/go.sum +++ b/server/go.sum @@ -1,7 +1,3 @@ -github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= -github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= -github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -9,6 +5,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set/v2 v2.7.0 h1:gIloKvD7yH2oip4VLhsv3JyLLFnC0Y2mlusgcvJYW5k= github.com/deckarep/golang-set/v2 v2.7.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/elliotchance/orderedmap/v3 v3.1.0 h1:j4DJ5ObEmMBt/lcwIecKcoRxIQUEnw0L804lXYDt/pg= +github.com/elliotchance/orderedmap/v3 v3.1.0/go.mod h1:G+Hc2RwaZvJMcS4JpGCOyViCnGeKf0bTYCGTO4uhjSo= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -31,7 +29,6 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -40,8 +37,6 @@ github.com/luskaner/ageLANServer/common v0.0.0-20241204232812-4e96c2e19aea h1:Gq github.com/luskaner/ageLANServer/common v0.0.0-20241204232812-4e96c2e19aea/go.mod h1:EjZJzA8Wxds6K07qIRsOroRZy1bj1l7ak8mzO0tQnrE= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= @@ -80,8 +75,6 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= -github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= diff --git a/server/internal/SafeMap.go b/server/internal/SafeMap.go index 9e2bcf2..f1151e3 100644 --- a/server/internal/SafeMap.go +++ b/server/internal/SafeMap.go @@ -1,6 +1,7 @@ package internal import ( + "iter" "sync" ) @@ -40,28 +41,15 @@ func (sm *SafeMap[K, V]) Len() int { return len(sm.data) } -func (sm *SafeMap[K, V]) IterValues() []V { - sm.mu.RLock() - defer sm.mu.RUnlock() - - result := make([]V, 0, len(sm.data)) - - for _, v := range sm.data { - result = append(result, v) - } - - return result -} - -func (sm *SafeMap[K, V]) IterKeys() []K { - sm.mu.RLock() - defer sm.mu.RUnlock() - - result := make([]K, 0, len(sm.data)) +func (sm *SafeMap[K, V]) Iter() iter.Seq2[K, V] { + return func(yield func(K, V) bool) { + sm.mu.RLock() + defer sm.mu.RUnlock() - for k := range sm.data { - result = append(result, k) + for k, v := range sm.data { + if !yield(k, v) { + return + } + } } - - return result } diff --git a/server/internal/SafeSet.go b/server/internal/SafeSet.go deleted file mode 100644 index 2a516fb..0000000 --- a/server/internal/SafeSet.go +++ /dev/null @@ -1,54 +0,0 @@ -package internal - -import ( - "sync" -) - -type SafeSet[K comparable] struct { - mu sync.RWMutex - data map[K]any -} - -func NewSafeSet[K comparable]() *SafeSet[K] { - return &SafeSet[K]{ - data: make(map[K]any), - } -} - -func (sm *SafeSet[K]) Add(key K) { - sm.mu.Lock() - defer sm.mu.Unlock() - sm.data[key] = nil -} - -func (sm *SafeSet[K]) Has(key K) bool { - sm.mu.RLock() - defer sm.mu.RUnlock() - _, ok := sm.data[key] - return ok -} - -func (sm *SafeSet[K]) Delete(key K) { - sm.mu.Lock() - defer sm.mu.Unlock() - delete(sm.data, key) -} - -func (sm *SafeSet[K]) Len() int { - sm.mu.RLock() - defer sm.mu.RUnlock() - return len(sm.data) -} - -func (sm *SafeSet[K]) Iter() <-chan K { - ch := make(chan K) - go func() { - sm.mu.RLock() - defer sm.mu.RUnlock() - for k := range sm.data { - ch <- k - } - close(ch) - }() - return ch -} diff --git a/server/internal/cmd/root.go b/server/internal/cmd/root.go index 45a0bd3..ae06308 100644 --- a/server/internal/cmd/root.go +++ b/server/internal/cmd/root.go @@ -47,7 +47,7 @@ var ( fmt.Println(err.Error()) os.Exit(common.ErrPidLock) } - gameSet := mapset.NewSet[string](viper.GetStringSlice("Games")...) + gameSet := mapset.NewThreadUnsafeSet[string](viper.GetStringSlice("Games")...) if gameSet.IsEmpty() { fmt.Println("No games specified") _ = lock.Unlock() diff --git a/server/internal/ip/ip.go b/server/internal/ip/ip.go index e76317d..e3af83f 100644 --- a/server/internal/ip/ip.go +++ b/server/internal/ip/ip.go @@ -71,7 +71,7 @@ func ResolveAddrs(listenIP net.IP, multicastIP net.IP, targetPort int, broadcast } func ResolveHosts(hosts []string) []net.IP { - ipsSet := mapset.NewSet[string]() + ipsSet := mapset.NewThreadUnsafeSet[string]() for _, host := range hosts { ip := net.ParseIP(host) if ip == nil { diff --git a/server/internal/middleware/game.go b/server/internal/middleware/game.go index ba27180..b3261c7 100644 --- a/server/internal/middleware/game.go +++ b/server/internal/middleware/game.go @@ -40,7 +40,7 @@ func GameMiddleware(next http.Handler) http.Handler { gameId = session.GetGameId() } } - gameSet := mapset.NewSet[string](viper.GetStringSlice("Games")...) + gameSet := mapset.NewThreadUnsafeSet[string](viper.GetStringSlice("Games")...) if !gameSet.ContainsOne(gameId) { http.Error(w, "Unavailable game type", http.StatusBadRequest) return diff --git a/server/internal/models/advertisement.go b/server/internal/models/advertisement.go index 85c8663..380645e 100644 --- a/server/internal/models/advertisement.go +++ b/server/internal/models/advertisement.go @@ -2,11 +2,11 @@ package models import ( "fmt" + mapset "github.com/deckarep/golang-set/v2" + "github.com/elliotchance/orderedmap/v3" "github.com/luskaner/ageLANServer/common" i "github.com/luskaner/ageLANServer/server/internal" "github.com/luskaner/ageLANServer/server/internal/routes/game/advertisement/shared" - "github.com/wk8/go-ordered-map/v2" - "math" "sync" "time" ) @@ -221,7 +221,7 @@ func (advs *MainAdvertisements) Store(advFrom *shared.AdvertisementHostRequest) func() { i.RngLock.Lock() defer i.RngLock.Unlock() - id = i.Rng.Int31n(math.MaxInt32) + id = i.Rng.Int32() }() _, exists := advs.store.Load(id) if !exists { @@ -234,14 +234,14 @@ func (advs *MainAdvertisements) Store(advFrom *shared.AdvertisementHostRequest) func() { i.RngLock.Lock() defer i.RngLock.Unlock() - adv.ip = fmt.Sprintf("/10.0.11.%d", i.Rng.Intn(254)+1) + adv.ip = fmt.Sprintf("/10.0.11.%d", i.Rng.IntN(254)+1) }() adv.relayRegion = advFrom.RelayRegion adv.party = advFrom.Party adv.race = advFrom.Race adv.team = advFrom.Team adv.statGroup = advFrom.StatGroup - adv.peers = orderedmap.New[*MainUser, *MainPeer]() + adv.peers = orderedmap.NewOrderedMap[*MainUser, *MainPeer]() adv.chat = make([]*MainMessage, 0) advs.update(adv, &shared.AdvertisementUpdateRequest{ Id: adv.id, @@ -346,7 +346,7 @@ func (advs *MainAdvertisements) NewPeer(adv *MainAdvertisement, u *MainUser, rac user: u, race: race, team: team, - invites: i.NewSafeSet[*MainUser](), + invites: mapset.NewSet[*MainUser](), lock: &sync.RWMutex{}, } userId := peer.user.GetId() @@ -382,8 +382,8 @@ func (advs *MainAdvertisements) Delete(adv *MainAdvertisement) { defer adv.lock.Unlock() advs.store.Delete(adv.id) adv.host.SetAdvertisement(nil) - for el := adv.peers.Oldest(); el != nil; el = el.Next() { - el.Value.GetUser().SetAdvertisement(nil) + for el := range adv.peers.Values() { + el.GetUser().SetAdvertisement(nil) } } @@ -408,9 +408,8 @@ func (adv *MainAdvertisement) UpdatePlatformSessionId(sessionId uint64) { func (adv *MainAdvertisement) EncodePeers() i.A { var peers = make(i.A, adv.peers.Len()) j := 0 - for el := adv.peers.Oldest(); el != nil; el = el.Next() { - p := el.Value - userId := el.Key.GetId() + for key, p := range adv.peers.AllFromFront() { + userId := key.GetId() func() { adv.peerLock.RLock(userId) defer adv.peerLock.RUnlock(userId) @@ -497,7 +496,7 @@ func (adv *MainAdvertisement) Encode(gameId string) i.A { func (advs *MainAdvertisements) FindAdvertisements(matches func(adv *MainAdvertisement) bool) []*MainAdvertisement { var res []*MainAdvertisement - for _, adv := range advs.store.IterValues() { + for _, adv := range advs.store.Iter() { func() { adv.lock.RLock() defer adv.lock.RUnlock() diff --git a/server/internal/models/age1/game.go b/server/internal/models/age1/game.go index 21c630f..3090c66 100644 --- a/server/internal/models/age1/game.go +++ b/server/internal/models/age1/game.go @@ -7,5 +7,5 @@ import ( ) func CreateGame() models.Game { - return models.CreateGame(common.GameAoE1, mapset.NewSet[string]("itemDefinitions.json")) + return models.CreateGame(common.GameAoE1, mapset.NewThreadUnsafeSet[string]("itemDefinitions.json")) } diff --git a/server/internal/models/age2/game.go b/server/internal/models/age2/game.go index af62f33..668fe92 100644 --- a/server/internal/models/age2/game.go +++ b/server/internal/models/age2/game.go @@ -7,5 +7,5 @@ import ( ) func CreateGame() models.Game { - return models.CreateGame(common.GameAoE2, mapset.NewSet[string]("itemBundleItems.json", "itemDefinitions.json")) + return models.CreateGame(common.GameAoE2, mapset.NewThreadUnsafeSet[string]("itemBundleItems.json", "itemDefinitions.json")) } diff --git a/server/internal/models/age3/game.go b/server/internal/models/age3/game.go index bd7bd2d..f702574 100644 --- a/server/internal/models/age3/game.go +++ b/server/internal/models/age3/game.go @@ -7,5 +7,5 @@ import ( ) func CreateGame() models.Game { - return models.CreateGame(common.GameAoE3, mapset.NewSet[string]("itemDefinitions.json")) + return models.CreateGame(common.GameAoE3, mapset.NewThreadUnsafeSet[string]("itemDefinitions.json")) } diff --git a/server/internal/models/chatChannel.go b/server/internal/models/chatChannel.go index 3100e91..9c1571d 100644 --- a/server/internal/models/chatChannel.go +++ b/server/internal/models/chatChannel.go @@ -1,8 +1,9 @@ package models import ( + "github.com/elliotchance/orderedmap/v3" "github.com/luskaner/ageLANServer/server/internal" - orderedmap "github.com/wk8/go-ordered-map/v2" + "iter" "strconv" "sync" ) @@ -43,8 +44,8 @@ func (channel *MainChatChannel) Encode() internal.A { func (channel *MainChatChannel) encodeUsers() internal.A { i := 0 c := make(internal.A, channel.users.Len()) - for el := channel.users.Oldest(); el != nil; el = el.Next() { - c[i] = internal.A{0, el.Value.GetProfileInfo(false)} + for el := range channel.users.Values() { + c[i] = internal.A{0, el.GetProfileInfo(false)} i++ } return c @@ -56,20 +57,17 @@ func (channel *MainChatChannel) EncodeUsers() internal.A { return channel.encodeUsers() } -func (channel *MainChatChannel) getUsers() []*MainUser { - i := 0 - users := make([]*MainUser, channel.users.Len()) - for el := channel.users.Oldest(); el != nil; el = el.Next() { - users[i] = el.Value - i++ - } - return users -} +func (channel *MainChatChannel) GetUsers() iter.Seq[*MainUser] { + return func(yield func(user *MainUser) bool) { + channel.usersLock.RLock() + defer channel.usersLock.RUnlock() -func (channel *MainChatChannel) GetUsers() []*MainUser { - channel.usersLock.RLock() - defer channel.usersLock.RUnlock() - return channel.getUsers() + for v := range channel.users.Values() { + if !yield(v) { + return + } + } + } } func (channel *MainChatChannel) AddUser(user *MainUser) internal.A { @@ -89,7 +87,7 @@ func (channel *MainChatChannel) RemoveUser(user *MainUser) { func (channel *MainChatChannel) HasUser(user *MainUser) bool { channel.usersLock.RLock() defer channel.usersLock.RUnlock() - _, ok := channel.users.Load(user.GetId()) + _, ok := channel.users.Get(user.GetId()) return ok } @@ -104,13 +102,13 @@ type MainChatChannels struct { } func (channels *MainChatChannels) Initialize(chatChannels map[string]MainChatChannel) { - channels.index = orderedmap.New[int32, *MainChatChannel]() + channels.index = orderedmap.NewOrderedMap[int32, *MainChatChannel]() for id, channel := range chatChannels { idInt, err := strconv.ParseInt(id, 10, 32) if err != nil { panic(err) } - channel.users = orderedmap.New[int32, *MainUser]() + channel.users = orderedmap.NewOrderedMap[int32, *MainUser]() channel.usersLock = &sync.RWMutex{} channel.messagesLock = &sync.RWMutex{} channel.Id = int32(idInt) @@ -121,8 +119,8 @@ func (channels *MainChatChannels) Initialize(chatChannels map[string]MainChatCha func (channels *MainChatChannels) Encode() internal.A { c := make(internal.A, channels.index.Len()) i := 0 - for el := channels.index.Oldest(); el != nil; el = el.Next() { - c[i] = el.Value.Encode() + for el := range channels.index.Values() { + c[i] = el.Encode() i++ } return c diff --git a/server/internal/models/credentials.go b/server/internal/models/credentials.go index b7368bc..7291d48 100644 --- a/server/internal/models/credentials.go +++ b/server/internal/models/credentials.go @@ -34,7 +34,9 @@ func (creds *Credentials) generateSignature() string { func() { i.RngLock.Lock() defer i.RngLock.Unlock() - i.Rng.Read(b) + for j := 0; j < 32; j++ { + b[j] = byte(i.Rng.UintN(256)) + } }() var hash [32]byte func() { @@ -87,10 +89,9 @@ func (cred *Credential) GetKey() string { } func (creds *Credentials) removeCredentialsExpired() { - signaturesToRemove := mapset.NewSet[string]() - for _, credKey := range creds.store.IterKeys() { - credValue, ok := creds.store.Load(credKey) - if ok && credValue.Expired() { + signaturesToRemove := mapset.NewThreadUnsafeSet[string]() + for credKey, credValue := range creds.store.Iter() { + if credValue.Expired() { signaturesToRemove.Add(credKey) } } diff --git a/server/internal/models/peer.go b/server/internal/models/peer.go index 0673093..50a66f9 100644 --- a/server/internal/models/peer.go +++ b/server/internal/models/peer.go @@ -1,6 +1,7 @@ package models import ( + mapset "github.com/deckarep/golang-set/v2" i "github.com/luskaner/ageLANServer/server/internal" "sync" ) @@ -10,7 +11,7 @@ type MainPeer struct { user *MainUser race int32 team int32 - invites *i.SafeSet[*MainUser] + invites mapset.Set[*MainUser] lock *sync.RWMutex } @@ -53,11 +54,11 @@ func (peer *MainPeer) Invite(user *MainUser) { } func (peer *MainPeer) Uninvite(user *MainUser) { - peer.invites.Delete(user) + peer.invites.Remove(user) } func (peer *MainPeer) IsInvited(user *MainUser) bool { - return peer.invites.Has(user) + return peer.invites.ContainsOne(user) } func (peer *MainPeer) Update(race int32, team int32) { diff --git a/server/internal/models/resources.go b/server/internal/models/resources.go index a561e60..bf94471 100644 --- a/server/internal/models/resources.go +++ b/server/internal/models/resources.go @@ -4,11 +4,12 @@ import ( "encoding/json" "fmt" mapset "github.com/deckarep/golang-set/v2" + "github.com/elliotchance/orderedmap/v3" i "github.com/luskaner/ageLANServer/server/internal" - orderedmap "github.com/wk8/go-ordered-map/v2" "net/http" "os" "path/filepath" + "regexp" "strings" ) @@ -34,7 +35,7 @@ func (r *MainResources) Initialize(gameId string, keyedFilenames mapset.Set[stri r.KeyedFiles = make(map[string][]byte) r.nameToSignature = make(map[string]string) r.keyedFilenames = keyedFilenames - r.Login = orderedmap.New[string, string]() + r.Login = orderedmap.NewOrderedMap[string, string]() r.initializeLogin(gameId) r.initializeChatChannels(gameId) r.initializeResponses(gameId) @@ -57,15 +58,10 @@ func (r *MainResources) initializeLogin(gameId string) { if err != nil { panic(err) } - err = json.Unmarshal(data, r.Login) - if err != nil { - panic(err) - } - r.LoginData = make([]i.A, r.Login.Len()) - j := 0 - for el := r.Login.Oldest(); el != nil; el = el.Next() { - r.LoginData[j] = i.A{el.Key, el.Value} - j++ + re := regexp.MustCompile(`"([^"]*)"`) + matches := re.FindAllStringSubmatch(string(data), -1) + for j := 0; j < len(matches)-1; j += 2 { + r.Login.Set(matches[j][1], matches[j+1][1]) } } @@ -81,11 +77,10 @@ func (r *MainResources) initializeResponses(gameId string) { continue } if r.keyedFilenames.ContainsOne(name) { - var result = orderedmap.New[string, any]() - err = json.Unmarshal(data, result) - if err == nil { - rawSignature, _ := result.Get("dataSignature") - serverSignature := rawSignature.(string) + re := regexp.MustCompile(`"dataSignature"\s*:\s*"(.*?)"`) + matches := re.FindStringSubmatch(string(data)) + if len(matches) == 1 { + serverSignature := matches[1] r.KeyedFiles[name] = data r.nameToSignature[name] = serverSignature } diff --git a/server/internal/models/session.go b/server/internal/models/session.go index b7b77b4..12994dc 100644 --- a/server/internal/models/session.go +++ b/server/internal/models/session.go @@ -32,7 +32,7 @@ func generateSessionId() string { func() { internal.RngLock.Lock() defer internal.RngLock.Unlock() - sessionId[i] = sessionLetters[internal.Rng.Intn(len(sessionLetters))] + sessionId[i] = sessionLetters[internal.Rng.IntN(len(sessionLetters))] }() } sessionIdStr := string(sessionId) diff --git a/server/internal/models/user.go b/server/internal/models/user.go index 7d5e0cd..a45a948 100644 --- a/server/internal/models/user.go +++ b/server/internal/models/user.go @@ -8,7 +8,7 @@ import ( "github.com/spf13/viper" "hash" "hash/fnv" - "math/rand" + "math/rand/v2" "net" "strconv" "sync" @@ -27,7 +27,7 @@ type MainUser struct { isXbox bool presence int8 presenceLock *sync.RWMutex - chatChannels map[int32]*MainChatChannel + chatChannels *i.SafeMap[int32, *MainChatChannel] advertisement *MainAdvertisement advertisementLock *sync.RWMutex } @@ -54,42 +54,42 @@ func (users *MainUsers) generate(identifier string, isXbox bool, platformUserId seed = binary.BigEndian.Uint64(hsh) users.hasher.Reset() }() - rng := rand.New(rand.NewSource(int64(seed))) + rng := rand.New(rand.NewPCG(seed, seed)) return &MainUser{ - id: rng.Int31(), - statId: rng.Int31(), - profileId: rng.Int31(), + id: rng.Int32(), + statId: rng.Int32(), + profileId: rng.Int32(), profileMetadata: profileMetadata, profileUintFlag1: profileUIntFlag1, - reliclink: rng.Int31(), + reliclink: rng.Int32(), alias: alias, platformUserId: platformUserId, isXbox: isXbox, presenceLock: &sync.RWMutex{}, - chatChannels: map[int32]*MainChatChannel{}, + chatChannels: i.NewSafeMap[int32, *MainChatChannel](), advertisementLock: &sync.RWMutex{}, } } func generatePlatformUserIdSteam(rng *rand.Rand) uint64 { - Z := rng.Int63n(1 << 31) + Z := rng.Int64N(1 << 31) Y := Z % 2 id := Z*2 + Y + 76561197960265728 return uint64(id) } func generateFullPlatformUserIdXbox(platformUserId int64) string { - rng := rand.New(rand.NewSource(platformUserId)) + rng := rand.New(rand.NewPCG(uint64(platformUserId), uint64(platformUserId))) const chars = "0123456789ABCDEF" id := make([]byte, 40) for j := range id { - id[j] = chars[rng.Intn(len(chars))] + id[j] = chars[rng.IntN(len(chars))] } return string(id) } func generatePlatformUserIdXbox(rng *rand.Rand) uint64 { - return uint64(rng.Int63n(9e15) + 1e15) + return uint64(rng.Int64N(9e15) + 1e15) } func (users *MainUsers) GetOrCreateUser(gameId string, remoteAddr string, isXbox bool, platformUserId uint64, alias string) *MainUser { @@ -100,7 +100,8 @@ func (users *MainUsers) GetOrCreateUser(gameId string, remoteAddr string, isXbox if ip != nil { ipV4 := ip.To4() if ipV4 != nil { - rng := rand.New(rand.NewSource(int64(binary.BigEndian.Uint32(ipV4)))) + seed := uint64(binary.BigEndian.Uint32(ipV4)) + rng := rand.New(rand.NewPCG(seed, seed)) if isXbox { platformUserId = generatePlatformUserIdXbox(rng) } else { @@ -179,7 +180,7 @@ func (u *MainUser) GetPlatformUserID() uint64 { } func (users *MainUsers) GetUserByStatId(id int32) (*MainUser, bool) { - for _, u := range users.store.IterValues() { + for _, u := range users.store.Iter() { if u.statId == id { return u, true } @@ -188,7 +189,7 @@ func (users *MainUsers) GetUserByStatId(id int32) (*MainUser, bool) { } func (users *MainUsers) GetUserById(id int32) (*MainUser, bool) { - for _, u := range users.store.IterValues() { + for _, u := range users.store.Iter() { if u.id == id { return u, true } @@ -196,14 +197,14 @@ func (users *MainUsers) GetUserById(id int32) (*MainUser, bool) { return nil, false } -func (users *MainUsers) GetUserIds() []int32 { - userIds := make([]int32, users.store.Len()) - j := 0 - for _, u := range users.store.IterValues() { - userIds[j] = u.GetId() - j++ +func (users *MainUsers) GetUserIds() func(func(int32) bool) { + return func(yield func(int32) bool) { + for _, u := range users.store.Iter() { + if !yield(u.GetId()) { + return + } + } } - return userIds } func (u *MainUser) GetExtraProfileInfo() i.A { @@ -234,7 +235,7 @@ func (u *MainUser) GetProfileInfo(includePresence bool) i.A { func() { i.RngLock.Lock() defer i.RngLock.Unlock() - randomTimeDiff = i.Rng.Int63n(300-50+1) + 50 + randomTimeDiff = i.Rng.Int64N(300-50+1) + 50 }() profileInfo := i.A{ time.Now().UTC().Unix() - randomTimeDiff, @@ -293,27 +294,22 @@ func (u *MainUser) GetAdvertisement() *MainAdvertisement { } func (u *MainUser) JoinChatChannel(channel *MainChatChannel) i.A { - u.chatChannels[channel.GetId()] = channel + u.chatChannels.Store(channel.GetId(), channel) return channel.AddUser(u) } func (u *MainUser) LeaveChatChannel(channel *MainChatChannel) { - delete(u.chatChannels, channel.GetId()) + u.chatChannels.Delete(channel.GetId()) channel.RemoveUser(u) } -func (u *MainUser) GetChannels() []*MainChatChannel { - channels := make([]*MainChatChannel, len(u.chatChannels)) - j := 0 - for _, channel := range u.chatChannels { - channels[j] = channel - j++ - } - return channels +func (u *MainUser) GetChannels() func(func(int32, *MainChatChannel) bool) { + return u.chatChannels.Iter() } func (u *MainUser) SendChatChannelMessage(channel *MainChatChannel, text string) { - u.chatChannels[channel.GetId()].AddMessage(u, text) + storedChannel, _ := u.chatChannels.Load(channel.GetId()) + storedChannel.AddMessage(u, text) } func (u *MainUser) SetAdvertisement(adv *MainAdvertisement) { @@ -322,20 +318,9 @@ func (u *MainUser) SetAdvertisement(adv *MainAdvertisement) { u.advertisement = adv } -func (users *MainUsers) getUsers() []*MainUser { - us := make([]*MainUser, users.store.Len()) - j := 0 - for _, u := range users.store.IterValues() { - us[j] = u - j++ - } - return us -} - func (users *MainUsers) GetProfileInfo(includePresence bool, matches func(user *MainUser) bool) []i.A { - us := users.getUsers() var presenceData = make([]i.A, 0) - for _, u := range us { + for _, u := range users.store.Iter() { if matches(u) { presenceData = append(presenceData, u.GetProfileInfo(includePresence)) } diff --git a/server/internal/rng.go b/server/internal/rng.go index 16d3b21..4a873f4 100644 --- a/server/internal/rng.go +++ b/server/internal/rng.go @@ -1,11 +1,12 @@ package internal import ( - "math/rand" + "math/rand/v2" "sync" "time" ) -var src = rand.NewSource(time.Now().UnixNano()) +var seed = uint64(time.Now().UnixNano()) +var src = rand.NewPCG(seed, seed) var Rng = rand.New(src) var RngLock = sync.Mutex{} diff --git a/server/internal/routes/game/advertisement/getAdvertisements.go b/server/internal/routes/game/advertisement/getAdvertisements.go index 9438b56..afd680a 100644 --- a/server/internal/routes/game/advertisement/getAdvertisements.go +++ b/server/internal/routes/game/advertisement/getAdvertisements.go @@ -5,6 +5,7 @@ import ( i "github.com/luskaner/ageLANServer/server/internal" "github.com/luskaner/ageLANServer/server/internal/models" "net/http" + "slices" ) func GetAdvertisements(w http.ResponseWriter, r *http.Request) { @@ -18,12 +19,7 @@ func GetAdvertisements(w http.ResponseWriter, r *http.Request) { game := models.G(r) title := game.Title() advs := models.G(r).Advertisements().FindAdvertisementsEncoded(title, func(adv *models.MainAdvertisement) bool { - for _, advId := range advsIds { - if adv.GetId() == advId { - return true - } - } - return false + return slices.Contains(advsIds, adv.GetId()) }) if advs == nil { i.JSON(&w, diff --git a/server/internal/routes/game/advertisement/updatePlatformLobbyID.go b/server/internal/routes/game/advertisement/updatePlatformLobbyID.go index 26ae893..0143d06 100644 --- a/server/internal/routes/game/advertisement/updatePlatformLobbyID.go +++ b/server/internal/routes/game/advertisement/updatePlatformLobbyID.go @@ -38,11 +38,11 @@ func UpdatePlatformLobbyID(w http.ResponseWriter, r *http.Request) { adv.UpdatePlatformSessionId(req.PlatformSessionId) message := i.A{req.MatchID, "0", req.PlatformSessionId} - for el := adv.GetPeers().Oldest(); el != nil; el = el.Next() { - if el.Value == peer { + for el := range adv.GetPeers().Values() { + if el == peer { continue } - if currentSess, ok := models.GetSessionByUserId(el.Value.GetUser().GetId()); ok { + if currentSess, ok := models.GetSessionByUserId(el.GetUser().GetId()); ok { wss.SendOrStoreMessage( currentSess, "PlatformSessionUpdateMessage", diff --git a/server/internal/routes/game/advertisement/updateState.go b/server/internal/routes/game/advertisement/updateState.go index ba43420..5e136d5 100644 --- a/server/internal/routes/game/advertisement/updateState.go +++ b/server/internal/routes/game/advertisement/updateState.go @@ -40,8 +40,7 @@ func UpdateState(w http.ResponseWriter, r *http.Request) { sessions := make([]*models.Session, peersLen) advEncoded := adv.Encode(gameTitle) j := 0 - for el := adv.GetPeers().Oldest(); el != nil; el = el.Next() { - peer := el.Value + for peer := range adv.GetPeers().Values() { var sess *models.Session userId := peer.GetUser().GetId() sess, ok = models.GetSessionByUserId(userId) diff --git a/server/internal/routes/game/chat/joinChannel.go b/server/internal/routes/game/chat/joinChannel.go index f4d3e6a..29388dc 100644 --- a/server/internal/routes/game/chat/joinChannel.go +++ b/server/internal/routes/game/chat/joinChannel.go @@ -44,7 +44,7 @@ func JoinChannel(w http.ResponseWriter, r *http.Request) { } fmt.Println(r.RemoteAddr, string(jsonData)) staticResponse := i.A{chatChannelIdStr, i.A{0, user.GetProfileInfo(false)}} - for _, userId := range users.GetUserIds() { + for userId := range users.GetUserIds() { var existingUserSession *models.Session existingUserSession, ok = models.GetSessionByUserId(userId) if ok { diff --git a/server/internal/routes/game/chat/leaveChannel.go b/server/internal/routes/game/chat/leaveChannel.go index feeb2d6..a754df3 100644 --- a/server/internal/routes/game/chat/leaveChannel.go +++ b/server/internal/routes/game/chat/leaveChannel.go @@ -11,7 +11,7 @@ import ( func NotifyLeaveChannel(users *models.MainUsers, user *models.MainUser, chatChannelId int32) { staticResponse := i.A{strconv.Itoa(int(chatChannelId)), user.GetProfileInfo(false)} - for _, userId := range users.GetUserIds() { + for userId := range users.GetUserIds() { if userId == user.GetId() { continue } diff --git a/server/internal/routes/game/chat/sendText.go b/server/internal/routes/game/chat/sendText.go index 21cac7e..aa7d026 100644 --- a/server/internal/routes/game/chat/sendText.go +++ b/server/internal/routes/game/chat/sendText.go @@ -40,8 +40,7 @@ func SendText(w http.ResponseWriter, r *http.Request) { user.SendChatChannelMessage(chatChannel, text) i.JSON(&w, i.A{0}) staticResponse := i.A{chatChannelIdStr, strconv.Itoa(int(user.GetId())), "", text} - existingUsers := chatChannel.GetUsers() - for _, existingUser := range existingUsers { + for existingUser := range chatChannel.GetUsers() { var existingUserSession *models.Session existingUserSession, ok = models.GetSessionByUserId(existingUser.GetId()) wss.SendOrStoreMessage( diff --git a/server/internal/routes/game/login/logout.go b/server/internal/routes/game/login/logout.go index 4ff2f07..0885707 100644 --- a/server/internal/routes/game/login/logout.go +++ b/server/internal/routes/game/login/logout.go @@ -20,21 +20,18 @@ func Logout(w http.ResponseWriter, r *http.Request) { if adv := u.GetAdvertisement(); adv != nil { game.Advertisements().RemovePeer(adv, u) } - channels := u.GetChannels() - for j, channel := range channels { + for channelId, channel := range u.GetChannels() { u.LeaveChatChannel(channel) - chat.NotifyLeaveChannel(users, u, channel.GetId()) + chat.NotifyLeaveChannel(users, u, channelId) // AoE3 only takes into account the first notify in a readSession return // so delay each message by 100ms so they go in different responses // otherwise, it would appear as it left the first channel only - if j != len(channels)-1 { - time.Sleep(100 * time.Millisecond) - } + time.Sleep(100 * time.Millisecond) } relationship.ChangePresence(users, u, 0) if game.Title() == common.GameAoE3 { profileInfo := u.GetProfileInfo(false) - for _, user := range users.GetUserIds() { + for user := range users.GetUserIds() { if user != u.GetId() { currentSess, currentOk := models.GetSessionByUserId(user) if currentOk { diff --git a/server/internal/routes/game/login/platformlogin.go b/server/internal/routes/game/login/platformlogin.go index 6aac1c7..bf8f07d 100644 --- a/server/internal/routes/game/login/platformlogin.go +++ b/server/internal/routes/game/login/platformlogin.go @@ -30,8 +30,8 @@ func Platformlogin(w http.ResponseWriter, r *http.Request) { func() { i.RngLock.Lock() defer i.RngLock.Unlock() - t2 = t - i.Rng.Int63n(3600*2-3600+1) + 3600 - t3 = t - i.Rng.Int63n(3600*2-3600+1) + 3600 + t2 = t - i.Rng.Int64N(3600*2-3600+1) + 3600 + t3 = t - i.Rng.Int64N(3600*2-3600+1) + 3600 }() game := models.G(r) title := game.Title() @@ -46,7 +46,7 @@ func Platformlogin(w http.ResponseWriter, r *http.Request) { relationship.ChangePresence(users, u, 1) profileInfo := u.GetProfileInfo(false) if title == common.GameAoE3 { - for _, user := range users.GetUserIds() { + for user := range users.GetUserIds() { if user != u.GetId() { currentSess, currentOk := models.GetSessionByUserId(user) if currentOk { diff --git a/server/internal/routes/game/relationship/setPresence.go b/server/internal/routes/game/relationship/setPresence.go index c820168..8001a36 100644 --- a/server/internal/routes/game/relationship/setPresence.go +++ b/server/internal/routes/game/relationship/setPresence.go @@ -12,7 +12,7 @@ import ( func ChangePresence(users *models.MainUsers, user *models.MainUser, presence int8) { user.SetPresence(presence) profileInfo := i.A{user.GetProfileInfo(true)} - for _, u := range users.GetUserIds() { + for u := range users.GetUserIds() { sess, ok := models.GetSessionByUserId(u) if ok { wss.SendOrStoreMessage(