Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
ea71b46
Refactor eventbus & dep injection
Galaco Oct 29, 2025
0ea1b5c
Fixed timestep for physics
Galaco Oct 29, 2025
1f96748
Migrate to typed events
Galaco Oct 30, 2025
253602e
introduced ecs to model entities
Galaco Oct 30, 2025
ac00995
ecs migration part 1
Galaco Oct 30, 2025
c422f2d
ecs migration part 2
Galaco Oct 30, 2025
75ad733
add perf graph
Galaco Oct 30, 2025
7c59584
feat: autocomplete console commands
Galaco Oct 30, 2025
d5de745
fix: camera accel & rotation smoothness
Galaco Oct 30, 2025
f3a5d8a
feat: replace inkyblackness with cimgui
Galaco Oct 31, 2025
4929d7d
feat: cache renderable indices per material
Galaco Oct 31, 2025
ee217ed
migrate to bsp v1.0.0 beta
Galaco Nov 2, 2025
bee5d39
perf: batch staticprop rendering
Galaco Nov 2, 2025
9186577
feat: add debug renderer & migrate physmesh to it
Galaco Nov 2, 2025
cfd5c18
enhancement: add autocomplete to convars
Galaco Nov 2, 2025
56c55c6
feat: level loading screen
Galaco Nov 2, 2025
2e070ee
enhancement: add basic theme
Galaco Nov 2, 2025
4dc3f58
fix: prevent closure of menu when no level loaded
Galaco Nov 2, 2025
ec69351
feat: refactor gui into layered system
Galaco Nov 2, 2025
cfa2667
enhancement: simplified, clearer debug perf ui
Galaco Nov 2, 2025
d94b89b
feat: support multi-modelgroup meshes
Galaco Nov 2, 2025
173aec0
fix: most vertex triangle strips fixed
Galaco Nov 2, 2025
1199842
enhancement: add memory usage to perf panel
Galaco Nov 2, 2025
726d2b4
enhancement: optimize console log handling
Galaco Nov 2, 2025
47952d1
feat: worldvertextransition, displacement painting
Galaco Nov 2, 2025
2ce82b6
fix: orientation on some physics props wrong
Galaco Nov 3, 2025
53176e3
fix: bsp collision structure holes
Galaco Nov 3, 2025
46ef805
fix: some improvements to the displacement light rendering (not 100%)
Galaco Nov 3, 2025
8618761
fix: ci should run
Galaco Nov 4, 2025
eb37a49
feat: add autoexec.cfg support
Galaco Nov 4, 2025
f3b73f7
feat: initial player implementation
Galaco Nov 4, 2025
45b4afa
fix: some improvements to slope handling stucking
Galaco Nov 4, 2025
c591b9f
fix: further slope handling fixes
Galaco Nov 4, 2025
58bc85b
feat: additional slope fixes
Galaco Nov 5, 2025
70890c4
feat: skeleton server browser impl
Galaco Nov 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 0 additions & 28 deletions .circleci/config.yml

This file was deleted.

46 changes: 46 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: CI

on:
push:
branches: [ main, dev, master ]
pull_request:
branches: [ main, dev, master ]

jobs:
test:
name: Test
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.24'
cache: true

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
libbullet-dev \
libgl1-mesa-dev \
libxi-dev \
libxcursor-dev \
libxrandr-dev \
libxinerama-dev \
libxxf86vm-dev

- name: Download dependencies
run: go mod download

- name: Run tests
run: go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
file: ./coverage.txt
fail_ci_if_error: false
171 changes: 171 additions & 0 deletions browser/browser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package browser

import (
"fmt"
"sync"

"github.com/galaco/kero/messages"
)

// EventDispatcher interface defines how the browser communicates state changes
// This decouples the browser from the event system implementation
type EventDispatcher interface {
// DispatchTyped sends a typed event to all registered listeners
DispatchTyped(event interface{})
}

// ServerBrowser manages server browser state and operations
// It provides thread-safe access to query results and orchestrates async queries
type ServerBrowser struct {
mu sync.RWMutex

// Current query state
queryInProgress bool
currentAddress string

// Results storage
lastResult *ServerInfo
lastError error

// Query history (for future features like recent servers)
queryHistory []*ServerInfo

// Dependencies (injected for testability)
networkClient NetworkClient
eventBus EventDispatcher
}

// NewServerBrowser creates a new server browser with the given dependencies
func NewServerBrowser(networkClient NetworkClient, eventBus EventDispatcher) *ServerBrowser {
return &ServerBrowser{
networkClient: networkClient,
eventBus: eventBus,
queryHistory: make([]*ServerInfo, 0),
}
}

// Query initiates an asynchronous server query
// This method returns immediately and dispatches events when the query completes
func (b *ServerBrowser) Query(address string) error {
// Validate input
if address == "" {
return fmt.Errorf("address cannot be empty")
}

b.mu.Lock()
// Check if a query is already in progress
if b.queryInProgress {
b.mu.Unlock()
return fmt.Errorf("query already in progress for %s", b.currentAddress)
}

// Mark query as started
b.queryInProgress = true
b.currentAddress = address

// Clear previous results
b.lastResult = nil
b.lastError = nil
b.mu.Unlock()

// Dispatch query started event
if b.eventBus != nil {
b.eventBus.DispatchTyped(messages.BrowserQueryStartedEvent{Address: address})
}

// Execute query asynchronously
go b.executeQuery(address)

return nil
}

// executeQuery performs the actual query in a goroutine
// This is internal and should not be called directly
func (b *ServerBrowser) executeQuery(address string) {
// Query the server
info, err := b.networkClient.QueryServerInfo(address)

// Update state (thread-safe)
b.mu.Lock()
b.queryInProgress = false
b.currentAddress = ""
b.lastResult = info
b.lastError = err

// Add to history if successful
if err == nil && info != nil {
b.addToHistory(info)
}
b.mu.Unlock()

// Dispatch appropriate event
if b.eventBus != nil {
if err != nil {
b.eventBus.DispatchTyped(messages.BrowserQueryFailedEvent{
Address: address,
})
} else {
b.eventBus.DispatchTyped(messages.BrowserQueryCompletedEvent{
Address: address,
})
}
}
}

// addToHistory adds a query result to the history
// Must be called with lock held
func (b *ServerBrowser) addToHistory(info *ServerInfo) {
// Limit history to 100 entries
const maxHistory = 100

b.queryHistory = append(b.queryHistory, info)
if len(b.queryHistory) > maxHistory {
// Remove oldest entry
b.queryHistory = b.queryHistory[1:]
}
}

// GetLastResult returns the most recent query result
// Returns (result, error) - one will be nil depending on whether query succeeded
// Thread-safe: can be called from any goroutine
func (b *ServerBrowser) GetLastResult() (*ServerInfo, error) {
b.mu.RLock()
defer b.mu.RUnlock()
return b.lastResult, b.lastError
}

// IsQuerying returns true if a query is currently in progress
// Thread-safe: can be called from any goroutine
func (b *ServerBrowser) IsQuerying() bool {
b.mu.RLock()
defer b.mu.RUnlock()
return b.queryInProgress
}

// GetCurrentAddress returns the address being queried (if any)
// Returns empty string if no query is in progress
// Thread-safe: can be called from any goroutine
func (b *ServerBrowser) GetCurrentAddress() string {
b.mu.RLock()
defer b.mu.RUnlock()
return b.currentAddress
}

// GetQueryHistory returns a copy of the query history
// Thread-safe: can be called from any goroutine
func (b *ServerBrowser) GetQueryHistory() []*ServerInfo {
b.mu.RLock()
defer b.mu.RUnlock()

// Return a copy to prevent external modification
historyCopy := make([]*ServerInfo, len(b.queryHistory))
copy(historyCopy, b.queryHistory)
return historyCopy
}

// ClearHistory clears the query history
func (b *ServerBrowser) ClearHistory() {
b.mu.Lock()
defer b.mu.Unlock()
b.queryHistory = make([]*ServerInfo, 0)
}
Loading
Loading