Use gosimpleconf. Pull stuff out of main.go

main
Sean Hickey 2022-06-18 13:27:50 -07:00
parent 4ef88ad21f
commit 95d58d4525
12 changed files with 294 additions and 189 deletions

View File

@ -3,8 +3,8 @@ all: release
dependencies:
go mod tidy
build: linter dependencies
go build -x -v
debug: linter dependencies
go build -x -v -gcflags=all="-N -l"
release: linter dependencies
go build -x -v -ldflags "-s -w"

7
game.conf Normal file
View File

@ -0,0 +1,7 @@
game.title = Carpy Breakout
cpuprofile.enabled = false
cpuprofile.file = cpu.prof
log.writeToFile = false
log.file = output.log

1
go.mod
View File

@ -3,6 +3,7 @@ module gitea.wisellama.rocks/Wisellama/carpy-breakout
go 1.17
require (
gitea.wisellama.rocks/Wisellama/gosimpleconf v0.0.3
github.com/go-gl/gl v0.0.0-20211025173605-bda47ffaa784
github.com/go-gl/mathgl v1.0.0
github.com/veandco/go-sdl2 v0.4.10

2
go.sum
View File

@ -1,3 +1,5 @@
gitea.wisellama.rocks/Wisellama/gosimpleconf v0.0.3 h1:g4hcc7TjodjMNrSEQ3UO96HXvltHc2EOiudS+Wfle60=
gitea.wisellama.rocks/Wisellama/gosimpleconf v0.0.3/go.mod h1:kY9gQL8laVTe+tW0ue5bYb6QThw78d7mx6AHwQ5CIzc=
github.com/go-gl/gl v0.0.0-20211025173605-bda47ffaa784 h1:1Zi56D0LNfvkzM+BdoxKryvUEdyWO7LP8oRT+oSYJW0=
github.com/go-gl/gl v0.0.0-20211025173605-bda47ffaa784/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
github.com/go-gl/mathgl v1.0.0 h1:t9DznWJlXxxjeeKLIdovCOVJQk/GzDEL7h/h+Ro2B68=

197
main.go
View File

@ -1,195 +1,42 @@
package main
import (
"flag"
"log"
"os"
"runtime"
"runtime/pprof"
"gitea.wisellama.rocks/Wisellama/carpy-breakout/pkg/breakout"
"gitea.wisellama.rocks/Wisellama/carpy-breakout/pkg/globjects"
gl "github.com/go-gl/gl/v3.1/gles2"
"github.com/go-gl/mathgl/mgl32"
"github.com/veandco/go-sdl2/sdl"
"gitea.wisellama.rocks/Wisellama/carpy-breakout/pkg/config"
"gitea.wisellama.rocks/Wisellama/carpy-breakout/pkg/game"
"gitea.wisellama.rocks/Wisellama/gosimpleconf"
)
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
const gameTitle = "Carpy Breakout"
func main() {
flag.Parse()
// Initialize the random number generator
err := config.InitRNG()
if err != nil {
log.Fatalf("error initializing RNG: %v", err)
}
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
// Parse the config and setup logging
configMap, err := config.Configure()
if err != nil {
log.Fatalf("error in Configure: %v", err)
}
// Setup cpu profiling if configured
cpuProfile := gosimpleconf.Bool(configMap["cpuprofile.enabled"])
if cpuProfile {
f, err := os.Create(configMap["cpuprofile.file"])
if err != nil {
log.Fatal(err)
log.Fatalf("error creating file for cpuprofile: %v", err)
}
err = pprof.StartCPUProfile(f)
if err != nil {
log.Fatal(err)
log.Fatalf("error starting cpuprofile: %v", err)
}
defer pprof.StopCPUProfile()
}
setupLogging()
// TODO can potentially rework stuff to not need LockOSThread()
// here. SDL has the built-in sdl.Do(), but I would need to create
// my own similar thing for OpenGL. Essentially they both need to
// be locked to the main thread, but anything else can be in a
// goroutine.
runtime.LockOSThread()
runOpenGL()
}
func runOpenGL() {
var sdlInitFlags uint32 = sdl.INIT_TIMER | sdl.INIT_AUDIO | sdl.INIT_VIDEO | sdl.INIT_EVENTS | sdl.INIT_JOYSTICK | sdl.INIT_HAPTIC | sdl.INIT_GAMECONTROLLER // ignore sensor subsystem
err := sdl.Init(sdlInitFlags)
if err != nil {
log.Fatal(err)
}
defer sdl.Quit()
err = sdlSettings()
if err != nil {
log.Fatal(err)
}
gameWindow, err := breakout.NewGameWindow(gameTitle)
if err != nil {
log.Fatal(err)
}
defer gameWindow.Destroy()
// GL Init requires an active OpenGL context (e.g. the game window)
err = gl.Init()
if err != nil {
log.Fatalf("Failed to initialize OpenGL: %v", err)
}
glVersion := gl.GetString(gl.VERSION)
if glVersion == nil {
log.Printf("Error getting OpenGL version.\n")
}
log.Printf("OpenGL Version: %v\n", gl.GoStr(glVersion))
// Then we can setup the OpenGL specific stuff in the game window
err = gameWindow.GLInitDefault()
if err != nil {
log.Printf("error in gameWindow GLInitDefault: %v", err)
}
sunLight := globjects.NewPointLight(mgl32.Vec3{-10, 10, 30})
sunLight.GLInit(gameWindow.GLProgram)
camera := globjects.NewCamera(gameWindow.GLProgram)
camera.Position = mgl32.Vec3{0, 0, 30}
gameWindow.SetCamera(camera)
gameWindow.AddObject(camera)
// textureId, err := glhelpers.NewTexture("square.png")
// if err != nil {
// log.Fatal(err)
// }
// Brick Targets
targets := breakout.NewTargets(4, 5, mgl32.Vec3{0, 6, 0})
targets.GLInit(gameWindow.GLProgram)
gameWindow.SetTargets(targets)
for _, brick := range targets.Bricks {
gameWindow.AddObject(brick.Box)
}
gameWindowAABB := globjects.NewAABB(targets.TopRight, targets.TopRight.Mul(-1))
gameWindow.AABB = gameWindowAABB
// Side Walls
sideWallsMaterial := globjects.NewMaterial()
sideWallsMaterial.Color = mgl32.Vec4{0.3, 0.3, 0.3, 1}
sideWalls := breakout.NewSideWalls(gameWindowAABB, sideWallsMaterial)
sideWalls.GLInit(gameWindow.GLProgram)
gameWindow.SetSideWalls(sideWalls)
for _, wall := range sideWalls.Boxes {
gameWindow.AddObject(wall)
}
// Paddle
paddleMaterial := globjects.NewMaterial()
paddleMaterial.Color = mgl32.Vec4{0, 1, 0, 1}
paddleBox := globjects.NewBox(6.0, 1.0, 4.0, mgl32.Vec3{0, -8, 0}, paddleMaterial)
paddle := breakout.NewPaddle(paddleBox)
paddle.GLInit(gameWindow.GLProgram)
gameWindow.SetPaddle(paddle)
gameWindow.AddObject(paddle)
// Ball
ballMaterial := globjects.NewMaterial()
ballMaterial.Color = mgl32.Vec4{1, 1, 1, 1}
ballBox := globjects.NewBox(1, 1, 1, mgl32.Vec3{-8, 0, 0}, ballMaterial)
ball := breakout.NewBall(ballBox)
ball.GLInit(gameWindow.GLProgram)
gameWindow.SetBall(ball)
gameWindow.AddObject(ball)
ball.Box.Velocity = mgl32.Vec3{.1, -.1, 0}
glSettings()
for gameWindow.IsRunning() {
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
gameWindow.Update()
gameWindow.GLDraw()
gameWindow.SDLWindow.GLSwap()
gameWindow.HandleEvents()
sdl.Delay(16)
}
}
func setupLogging() {
// const logFile = "output.log"
// file, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
// if err != nil {
// log.Fatal(err)
// }
// log.SetOutput(file)
log.Println("=== START ===")
}
func glSettings() {
// Global options after setup is all done
gl.ClearColor(0.0, 0.0, 0.0, 1.0)
gl.Enable(gl.DEPTH_TEST)
gl.DepthFunc(gl.LESS)
gl.LineWidth(2.0)
}
func sdlSettings() error {
var err error
// Smooth
err = sdl.GLSetAttribute(sdl.GL_MULTISAMPLEBUFFERS, 1)
if err != nil {
log.Printf("error setting GL_MULTISAMPLEBUFFERS: %v", err)
return err
}
err = sdl.GLSetAttribute(sdl.GL_MULTISAMPLESAMPLES, 4)
if err != nil {
log.Printf("error setting GL_MULTISAMPLESAMPLES: %v", err)
return err
}
// Disable the Linux compositor flicker.
// https://github.com/mosra/magnum/issues/184#issuecomment-425952900
sdl.SetHint(sdl.HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0")
sdl.DisableScreenSaver()
// Capture the mouse for movement
sdl.SetRelativeMouseMode(true)
return nil
// Start the game
game.Run(configMap)
}

View File

@ -4,6 +4,7 @@ import (
"fmt"
"log"
"gitea.wisellama.rocks/Wisellama/carpy-breakout/pkg/config"
"gitea.wisellama.rocks/Wisellama/carpy-breakout/pkg/glhelpers"
"gitea.wisellama.rocks/Wisellama/carpy-breakout/pkg/globjects"
gl "github.com/go-gl/gl/v3.1/gles2"
@ -11,11 +12,6 @@ import (
"github.com/veandco/go-sdl2/sdl"
)
const defaultWindowWidth int32 = 800
const defaultWindowHeight int32 = 600
const defaultWindowFlags uint32 = sdl.WINDOW_SHOWN | sdl.WINDOW_RESIZABLE | sdl.WINDOW_OPENGL
const fullscreenWindowFlags uint32 = defaultWindowFlags | sdl.WINDOW_FULLSCREEN_DESKTOP
type GameWindow struct {
SDLWindow *sdl.Window
GLContext *sdl.GLContext
@ -47,9 +43,9 @@ func NewGameWindow(title string) (*GameWindow, error) {
title,
sdl.WINDOWPOS_UNDEFINED,
sdl.WINDOWPOS_UNDEFINED,
defaultWindowWidth,
defaultWindowHeight,
defaultWindowFlags)
config.SDL_WINDOW_WIDTH,
config.SDL_WINDOW_HEIGHT,
config.SDL_WINDOW_FLAGS)
if err != nil {
log.Println("Failed creating SDL window")
return nil, err
@ -101,14 +97,14 @@ func (w *GameWindow) Destroy() {
func (w *GameWindow) ToggleFullscreen() {
var err error
if w.fullscreen {
err = w.SDLWindow.SetFullscreen(defaultWindowFlags)
err = w.SDLWindow.SetFullscreen(config.SDL_WINDOW_FLAGS)
if err != nil {
log.Printf("error setting defaultWindowFlags: %v", err)
log.Printf("error setting default window flags: %v", err)
}
} else {
err = w.SDLWindow.SetFullscreen(fullscreenWindowFlags)
err = w.SDLWindow.SetFullscreen(config.SDL_FULLSCREEN_WINDOW_FLAGS)
if err != nil {
log.Printf("error setting fullscreenWindowFlags: %v", err)
log.Printf("error setting fullscreen window flags: %v", err)
}
}

23
pkg/config/config.go Normal file
View File

@ -0,0 +1,23 @@
package config
import (
"gitea.wisellama.rocks/Wisellama/gosimpleconf"
)
func Configure() (gosimpleconf.ConfigMap, error) {
var err error
configMap, err := gosimpleconf.Load("game.conf")
if err != nil {
return nil, err
}
flagMap := gosimpleconf.SetupFlagOverrides(configMap)
configMap = gosimpleconf.ParseFlags(configMap, flagMap)
err = SetupLogging(configMap)
if err != nil {
return nil, err
}
return configMap, nil
}

26
pkg/config/logging.go Normal file
View File

@ -0,0 +1,26 @@
package config
import (
"log"
"os"
"gitea.wisellama.rocks/Wisellama/gosimpleconf"
)
func SetupLogging(configMap gosimpleconf.ConfigMap) error {
writeToFile := gosimpleconf.Bool(configMap["log.writeToFile"])
if writeToFile {
logFile := configMap["log.file"]
file, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
log.SetOutput(file)
}
log.Printf("=== START %v ===", configMap["game.title"])
return nil
}

13
pkg/config/opengl.go Normal file
View File

@ -0,0 +1,13 @@
package config
import (
gl "github.com/go-gl/gl/v3.1/gles2"
)
func GlSettings() {
// Global options after setup is all done
gl.ClearColor(0.0, 0.0, 0.0, 1.0)
gl.Enable(gl.DEPTH_TEST)
gl.DepthFunc(gl.LESS)
gl.LineWidth(2.0)
}

24
pkg/config/random.go Normal file
View File

@ -0,0 +1,24 @@
package config
import (
crypto_rand "crypto/rand"
"encoding/binary"
"fmt"
math_rand "math/rand"
)
// InitRNG will grab some cryptographically secure bytes to use as the
// seed for the non-crypto random number generator. Suggested on
// StackOverflow to avoid time-based seeds:
// https://stackoverflow.com/a/54491783
func InitRNG() error {
var b [8]byte
_, err := crypto_rand.Read(b[:])
if err != nil {
err = fmt.Errorf("error seeding random number generator: %w", err)
return err
}
math_rand.Seed(int64(binary.LittleEndian.Uint64(b[:])))
return nil
}

43
pkg/config/sdl.go Normal file
View File

@ -0,0 +1,43 @@
package config
import (
"log"
"github.com/veandco/go-sdl2/sdl"
)
const (
SDL_INIT_FLAGS uint32 = sdl.INIT_TIMER | sdl.INIT_AUDIO | sdl.INIT_VIDEO | sdl.INIT_EVENTS | sdl.INIT_JOYSTICK | sdl.INIT_HAPTIC | sdl.INIT_GAMECONTROLLER // ignore sensor subsystem
SDL_WINDOW_FLAGS uint32 = sdl.WINDOW_SHOWN | sdl.WINDOW_RESIZABLE | sdl.WINDOW_OPENGL
SDL_FULLSCREEN_WINDOW_FLAGS uint32 = SDL_WINDOW_FLAGS | sdl.WINDOW_FULLSCREEN_DESKTOP
SDL_WINDOW_WIDTH int32 = 800
SDL_WINDOW_HEIGHT int32 = 600
)
func SdlSettings() error {
var err error
// Smooth
err = sdl.GLSetAttribute(sdl.GL_MULTISAMPLEBUFFERS, 1)
if err != nil {
log.Printf("error setting GL_MULTISAMPLEBUFFERS: %v", err)
return err
}
err = sdl.GLSetAttribute(sdl.GL_MULTISAMPLESAMPLES, 4)
if err != nil {
log.Printf("error setting GL_MULTISAMPLESAMPLES: %v", err)
return err
}
// Disable the Linux compositor flicker.
// https://github.com/mosra/magnum/issues/184#issuecomment-425952900
sdl.SetHint(sdl.HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0")
sdl.DisableScreenSaver()
// Capture the mouse for movement
sdl.SetRelativeMouseMode(true)
return nil
}

123
pkg/game/opengl.go Normal file
View File

@ -0,0 +1,123 @@
package game
import (
"log"
"runtime"
"gitea.wisellama.rocks/Wisellama/carpy-breakout/pkg/breakout"
"gitea.wisellama.rocks/Wisellama/carpy-breakout/pkg/config"
"gitea.wisellama.rocks/Wisellama/carpy-breakout/pkg/globjects"
"gitea.wisellama.rocks/Wisellama/gosimpleconf"
gl "github.com/go-gl/gl/v3.1/gles2"
"github.com/go-gl/mathgl/mgl32"
"github.com/veandco/go-sdl2/sdl"
)
func Run(configMap gosimpleconf.ConfigMap) {
// TODO can potentially rework stuff to not need LockOSThread()
// here. SDL has the built-in sdl.Do(), but I would need to create
// my own similar thing for OpenGL. Essentially they both need to
// be locked to the main thread, but anything else can be in a
// goroutine.
runtime.LockOSThread()
err := sdl.Init(config.SDL_INIT_FLAGS)
if err != nil {
log.Fatal(err)
}
defer sdl.Quit()
err = config.SdlSettings()
if err != nil {
log.Fatal(err)
}
gameWindow, err := breakout.NewGameWindow(configMap["game.title"])
if err != nil {
log.Fatal(err)
}
defer gameWindow.Destroy()
// GL Init requires an active OpenGL context (e.g. the game window)
err = gl.Init()
if err != nil {
log.Fatalf("Failed to initialize OpenGL: %v", err)
}
glVersion := gl.GetString(gl.VERSION)
if glVersion == nil {
log.Printf("Error getting OpenGL version.\n")
}
log.Printf("OpenGL Version: %v\n", gl.GoStr(glVersion))
// Then we can setup the OpenGL specific stuff in the game window
err = gameWindow.GLInitDefault()
if err != nil {
log.Printf("error in gameWindow GLInitDefault: %v", err)
}
sunLight := globjects.NewPointLight(mgl32.Vec3{-10, 10, 30})
sunLight.GLInit(gameWindow.GLProgram)
camera := globjects.NewCamera(gameWindow.GLProgram)
camera.Position = mgl32.Vec3{0, 0, 30}
gameWindow.SetCamera(camera)
gameWindow.AddObject(camera)
// textureId, err := glhelpers.NewTexture("square.png")
// if err != nil {
// log.Fatal(err)
// }
// Brick Targets
targets := breakout.NewTargets(4, 5, mgl32.Vec3{0, 6, 0})
targets.GLInit(gameWindow.GLProgram)
gameWindow.SetTargets(targets)
for _, brick := range targets.Bricks {
gameWindow.AddObject(brick.Box)
}
gameWindowAABB := globjects.NewAABB(targets.TopRight, targets.TopRight.Mul(-1))
gameWindow.AABB = gameWindowAABB
// Side Walls
sideWallsMaterial := globjects.NewMaterial()
sideWallsMaterial.Color = mgl32.Vec4{0.3, 0.3, 0.3, 1}
sideWalls := breakout.NewSideWalls(gameWindowAABB, sideWallsMaterial)
sideWalls.GLInit(gameWindow.GLProgram)
gameWindow.SetSideWalls(sideWalls)
for _, wall := range sideWalls.Boxes {
gameWindow.AddObject(wall)
}
// Paddle
paddleMaterial := globjects.NewMaterial()
paddleMaterial.Color = mgl32.Vec4{0, 1, 0, 1}
paddleBox := globjects.NewBox(6.0, 1.0, 4.0, mgl32.Vec3{0, -8, 0}, paddleMaterial)
paddle := breakout.NewPaddle(paddleBox)
paddle.GLInit(gameWindow.GLProgram)
gameWindow.SetPaddle(paddle)
gameWindow.AddObject(paddle)
// Ball
ballMaterial := globjects.NewMaterial()
ballMaterial.Color = mgl32.Vec4{1, 1, 1, 1}
ballBox := globjects.NewBox(1, 1, 1, mgl32.Vec3{-8, 0, 0}, ballMaterial)
ball := breakout.NewBall(ballBox)
ball.GLInit(gameWindow.GLProgram)
gameWindow.SetBall(ball)
gameWindow.AddObject(ball)
ball.Box.Velocity = mgl32.Vec3{.1, -.1, 0}
config.GlSettings()
for gameWindow.IsRunning() {
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
gameWindow.Update()
gameWindow.GLDraw()
gameWindow.SDLWindow.GLSwap()
gameWindow.HandleEvents()
sdl.Delay(16)
}
}