chore(all): resolve lint findings, update example plugin, refine docs and add LGPL licenses

This commit is contained in:
2026-10-07 00:57:35 +08:00
parent e8e9d77974
commit 8e8ddeddb6
388 changed files with 4638 additions and 3321 deletions
+2 -2
View File
@@ -21,7 +21,7 @@ import (
//go:embed web/index.html
var webFS embed.FS
var errGameAlreadyRunning = errors.New("Brown Dust II is already running")
var errGameAlreadyRunning = errors.New("Brown Dust II is already running") //nolint:staticcheck // ST1005
type Options struct {
InitialGameDir string
@@ -226,7 +226,7 @@ func (s *Studio) Resources(input Request) (Response, error) {
defer cancel()
policy, err := clientsetup.FetchResourcePolicy(ctx, nil, input.settings(), s.options.Versions)
message := "The client will use the release-locked official CDN"
if policy.Mode == clientconfig.CDNLocal {
if policy.Mode == clientconfig.CDNLocal { //nolint:staticcheck // QF1003
message = "Local resources verified"
} else if policy.Mode == clientconfig.CDNServer {
message = "Server resource policy verified"
+1 -1
View File
@@ -45,7 +45,7 @@ func Run(runOptions Options) error {
studio.Shutdown()
studio.log().Info("native client window closed")
},
Bind: []interface{}{studio},
Bind: []any{studio},
EnableDefaultContextMenu: false,
DragAndDrop: &options.DragAndDrop{
DisableWebViewDrop: true,
+26 -7
View File
@@ -4,6 +4,7 @@ package app
import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
@@ -42,7 +43,9 @@ func ShowFatalError(err error) {
if conversionErr != nil {
return
}
messageBoxW.Call(0, uintptr(unsafe.Pointer(message)), uintptr(unsafe.Pointer(title)), 0x10)
if result, _, callErr := messageBoxW.Call(0, uintptr(unsafe.Pointer(message)), uintptr(unsafe.Pointer(title)), 0x10); result == 0 {
log.Printf("Fatal error dialog could not be displayed: %v", callErr)
}
}
// CREATE_NO_WINDOW prevents console-subsystem helpers such as powershell.exe
@@ -74,7 +77,7 @@ func launchGame(target, proxyURL string) error {
return err
} else if running {
if !activateProcessWindow(processID, 5*time.Second) {
return fmt.Errorf("Brown Dust II is running, but its window could not be restored")
return fmt.Errorf("Brown Dust II is running, but its window could not be restored") //nolint:staticcheck // ST1005
}
return errGameAlreadyRunning
}
@@ -95,7 +98,11 @@ func windowsExecutableProcessID(name string) (uint32, bool, error) {
if err != nil {
return 0, false, err
}
defer windows.CloseHandle(snapshot)
defer func() {
if err := windows.CloseHandle(snapshot); err != nil {
log.Printf("Process snapshot cleanup failed: %v", err)
}
}()
entry := windows.ProcessEntry32{Size: uint32(unsafe.Sizeof(windows.ProcessEntry32{}))}
if err := windows.Process32First(snapshot, &entry); err != nil {
return 0, false, err
@@ -120,9 +127,14 @@ func activateProcessWindow(processID uint32, timeout time.Duration) bool {
iconic, _, _ := isIconicProc.Call(window)
if iconic != 0 {
const swRestore = 9
showWindowAsyncProc.Call(window, swRestore)
if result, _, callErr := showWindowAsyncProc.Call(window, swRestore); result == 0 {
log.Printf("Game window restore request failed: %v", callErr)
}
}
if result, _, _ := setForegroundWindowProc.Call(window); result == 0 {
// Windows may deny foreground activation even for a valid game window.
log.Print("Windows declined foreground activation of the game window")
}
setForegroundWindowProc.Call(window)
return true
}
if time.Now().After(deadline) {
@@ -136,7 +148,9 @@ func topLevelWindowForProcess(processID uint32) uintptr {
var found uintptr
callback := syscall.NewCallback(func(window uintptr, _ uintptr) uintptr {
var owner uint32
getWindowThreadProcessIDProc.Call(window, uintptr(unsafe.Pointer(&owner)))
if thread, _, _ := getWindowThreadProcessIDProc.Call(window, uintptr(unsafe.Pointer(&owner))); thread == 0 {
return 1 // The window disappeared during enumeration.
}
visible, _, _ := isWindowVisibleProc.Call(window)
if owner == processID && visible != 0 {
found = window
@@ -144,6 +158,11 @@ func topLevelWindowForProcess(processID uint32) uintptr {
}
return 1
})
enumWindowsProc.Call(callback, 0)
if result, _, callErr := enumWindowsProc.Call(callback, 0); result == 0 && found == 0 {
// A successful match deliberately stops enumeration and also returns zero.
if callErr != syscall.Errno(0) {
log.Printf("Game window enumeration failed: %v", callErr)
}
}
return found
}
+1 -1
View File
@@ -162,7 +162,7 @@ func Save(gameDir string, in Settings) (Settings, error) {
return Settings{}, fmt.Errorf("client config: create temporary config: %w", err)
}
temporaryPath := temporary.Name()
defer os.Remove(temporaryPath)
defer func() { _ = os.Remove(temporaryPath) }()
if err = temporary.Chmod(0o600); err == nil {
_, err = temporary.Write(data)
}
+1 -1
View File
@@ -76,7 +76,7 @@ func SavePreferences(gameDirectory string) error {
return fmt.Errorf("stage client preferences: %w", err)
}
temporaryPath := temporary.Name()
defer os.Remove(temporaryPath)
defer func() { _ = os.Remove(temporaryPath) }()
if err = temporary.Chmod(0o600); err == nil {
_, err = temporary.Write(data)
}
+1 -1
View File
@@ -25,7 +25,7 @@ func cryptPages(in []byte, encrypt bool) ([]byte, error) {
}
out := make([]byte, len(in))
for start := 0; start < len(in); start += PageSize {
var mode cipher.BlockMode = cipher.NewCBCEncrypter(block, Header)
mode := cipher.NewCBCEncrypter(block, Header)
if !encrypt {
mode = cipher.NewCBCDecrypter(block, Header)
}
+3 -6
View File
@@ -199,10 +199,7 @@ func urlInDB(p []byte) (string, error) {
break
}
i += start + len(marker)
limit := i + 128
if limit > len(p) {
limit = len(p)
}
limit := min(i+128, len(p))
if j := bytes.Index(p[i:limit], []byte("http")); j >= 0 {
at := i + j
if at+len(oldURL) <= len(p) {
@@ -229,7 +226,7 @@ func copyFile(src, dst string) error {
if err != nil {
return err
}
defer in.Close()
defer func() { _ = in.Close() }()
out, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o600)
if err != nil {
return err
@@ -433,7 +430,7 @@ func findIntro(file []byte) (textAsset, []byte, error) {
return textAsset{}, nil, fmt.Errorf("introdb: invalid object count %d", count)
}
var candidates []textAsset
for i := int32(0); i < count; i++ {
for i := range count {
// Since serialized version 14, Unity aligns object records to four bytes
// before their 64-bit path ID (not to an eight-byte boundary).
r.off = (r.off + 48 + 3) &^ 3
+1 -1
View File
@@ -125,7 +125,7 @@ func installData(spec Spec, pluginDir string, sourceData []byte) (Result, error)
return Result{}, fmt.Errorf("clientplugin: create temporary plugin: %w", err)
}
temporaryPath := temporary.Name()
defer os.Remove(temporaryPath)
defer func() { _ = os.Remove(temporaryPath) }()
if _, err = io.Copy(temporary, bytes.NewReader(sourceData)); err == nil {
err = temporary.Sync()
}
+3 -4
View File
@@ -126,7 +126,7 @@ func readCatalog(path string) ([]byte, error) {
if err != nil {
return nil, fmt.Errorf("open local ServerData catalog: %w", err)
}
defer file.Close()
defer func() { _ = file.Close() }()
raw, err := io.ReadAll(io.LimitReader(file, maximumCatalogBytes+1))
if err != nil {
return nil, fmt.Errorf("read local ServerData catalog: %w", err)
@@ -163,8 +163,7 @@ func localizeCatalog(raw []byte, release, aa string) ([]byte, error) {
remoteCount++
continue
}
if strings.HasPrefix(internalID, localCatalogPrefix) {
relative := strings.TrimPrefix(internalID, localCatalogPrefix)
if relative, ok := strings.CutPrefix(internalID, localCatalogPrefix); ok {
relative = filepath.FromSlash(strings.ReplaceAll(relative, "\\", "/"))
if info, err := os.Stat(filepath.Join(aa, relative)); err != nil || !info.Mode().IsRegular() {
return nil, fmt.Errorf("built-in Addressables catalog references a missing local bundle: %s", relative)
@@ -288,7 +287,7 @@ func replaceCatalog(path string, data []byte) error {
return err
}
temporaryPath := temporary.Name()
defer os.Remove(temporaryPath)
defer func() { _ = os.Remove(temporaryPath) }()
if err = temporary.Chmod(0o600); err == nil {
_, err = temporary.Write(data)
}
+3 -3
View File
@@ -209,7 +209,7 @@ func FetchResourcePolicy(ctx context.Context, client *http.Client, settings clie
if err != nil {
return ResourcePolicy{}, fmt.Errorf("request server resource policy: %w", err)
}
defer response.Body.Close()
defer func() { _ = response.Body.Close() }()
limited := io.LimitReader(response.Body, 64<<10)
responseBody, err := io.ReadAll(limited)
if err != nil {
@@ -272,10 +272,10 @@ func detectClientVersion(path string) (string, error) {
return "", fmt.Errorf("read Brown Dust II client version metadata: %w", err)
}
if !info.Mode().IsRegular() {
return "", errors.New("Brown Dust II client version metadata is not a regular file")
return "", errors.New("Brown Dust II client version metadata is not a regular file") //nolint:staticcheck // ST1005
}
if info.Size() <= 0 || info.Size() > 64<<20 {
return "", fmt.Errorf("Brown Dust II client version metadata has an invalid size: %d", info.Size())
return "", fmt.Errorf("Brown Dust II client version metadata has an invalid size: %d", info.Size()) //nolint:staticcheck // ST1005
}
data, err := os.ReadFile(path)
if err != nil {