Add basic SDL setup.

Add basic SDL rendering.
Add config parsing.
Add readme, license, and setup docs.

Builds successfully on Windows surprisingly.
main
Sean Hickey 2022-09-28 00:05:06 -07:00
parent 70d3ee92f4
commit 34e3635f86
14 changed files with 525 additions and 2 deletions

12
.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
# Emacs temp files
*~
# SDL libraries
SDL*.dll
SDL*.so
SDL*.a
# Build output
*.exe
output.log
project-ely

24
LICENSE.md Normal file
View File

@ -0,0 +1,24 @@
# BSD 2-Clause License
Copyright (c) 2022, Sean Hickey (Wisellama) and Carly Falconer (Cahley)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

37
Makefile Normal file
View File

@ -0,0 +1,37 @@
EXECUTABLE=project-ely
all: release
dependencies:
go mod tidy
debug: linter dependencies
go build -x -v -gcflags=all="-N -l"
release: linter dependencies
go build -x -v -ldflags "-s -w"
linter:
golangci-lint run
clean:
rm -f ${EXECUTABLE}
rm -f output.log
rm -f cpu.prof
cross_windows:
CGO_ENABLED=1 \
CC=x86_64-w64-mingw32-gcc \
GOOS=windows \
GOARCH=amd64 \
go build -x -v -ldflags "-s -w"
cross_windows_x86:
CGO_ENABLED=1 \
CC=i686-w64-mingw32-gcc \
GOOS=windows \
GOARCH=386 \
go build -x -v -ldflags "-s -w"
goimports_everything:
find . -name "*.go" -exec goimports -w {} \;

37
README.md Normal file
View File

@ -0,0 +1,37 @@
# Project Ely
This is some game project that we are working on for fun.
This project is written in Go using SDL2.
## Compiling
You can use the Makefile:
```sh
make release
```
Or you can build the project easily with Go directly:
```sh
go build
```
A release build can be build with these extra flags:
```sh
go build -x -v -ldflags "-s -w"
```
## Running
You may need the SDL2 libraries in order to run the resulting binary. Copy the SDL2 *.dll or *.so files into the same directory as the output executable and run the program.
## Docs
For initial environment setup, see the [Setup Docs][1].
## License
This project is licensed under the 2-Clause BSD license, a permissive open-source license. See [LICENSE.md][2] for more details.
[1]: docs/setup
[2]: LICENSE.md

23
config/config.go Normal file
View File

@ -0,0 +1,23 @@
package config
import (
"gitea.wisellama.rocks/Wisellama/gosimpleconf"
)
func Configure(filename string) (gosimpleconf.ConfigMap, error) {
var err error
configMap, err := gosimpleconf.Load(filename)
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
}

31
config/logging.go Normal file
View File

@ -0,0 +1,31 @@
package config
import (
"log"
"os"
"path/filepath"
"gitea.wisellama.rocks/Wisellama/gosimpleconf"
)
func SetupLogging(configMap gosimpleconf.ConfigMap) error {
writeToFile := gosimpleconf.Bool(configMap["log.writeToFile"])
if writeToFile {
logFile := configMap["log.file"]
filename, err := filepath.Abs(logFile)
if err != nil {
return err
}
file, err := os.OpenFile(filename, 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
}

24
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
}

27
config/sdl.go Normal file
View File

@ -0,0 +1,27 @@
package config
import (
"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_FULLSCREEN_WINDOW_FLAGS uint32 = SDL_WINDOW_FLAGS | sdl.WINDOW_FULLSCREEN_DESKTOP
SDL_WINDOW_WIDTH int32 = 800
SDL_WINDOW_HEIGHT int32 = 600
)
func SdlSettings() error {
// 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
}

View File

@ -0,0 +1,98 @@
# Go with SDL on Windows
Windows is a bit of a different beast when it comes to setting up a development environment. Hopefully this documentation helps get you started. This guide tries to detail everything you would need to download and configure to get this to work.
As a side note, there is a cool tool called `Scoop` that you could use to install a lot of these tools. However, I ran into an issue using the Scoop versions of SDL2 because the directory names were different from what the header files were expecting. Below I just installed everything manually instead.
## Install Git
If you don't have Git yet, you can download it here.
https://git-scm.com/
Run through the installer like normal. There are a lot of config options in the installer, and most of the defaults will work fine. You can change them later in your `.gitconfig` file if needed.
## Download the project
You can now use Git to clone the project.
### IDE
Some IDEs like VSCode can clone a Git repository inside of them. You might be able to use that if it's easier.
### Git Bash
You can use the Git Bash terminal to do this too. Open up `Git Bash`. This is a Bash terminal configured to work with Git. Go into some directory where you want to keep your coding projects, and then run the following command.
```sh
mkdir projects
cd projects
git clone ssh://git@gitea.wisellama.rocks:222/Project-Ely/project-ely.git
```
## Install Go
We need the Go compiler. Download it and run through the installer like normal.
https://go.dev/
## Install GCC
GCC is a C compiler (well technically a the GNU Compiler Collection, but we want it for the C compiler part). Go needs GCC's C compiler so that it can compile the low-level code for the SDL libraries.
GCC on Windows is a bit of a special thing. We'll want to install Mingw-64 which is the Windows port of GCC.
https://www.mingw-w64.org/downloads/
You will want to download the "x86_64 win32 seh" version such as this one (ideally the latest version available, just providing a full link as an example).
https://github.com/niXman/mingw-builds-binaries/releases/download/12.2.0-rt_v10-rev0/x86_64-12.2.0-release-win32-seh-rt_v10-rev0.7z
There is no installer for this, so we just need to put this in a folder that we'll remember. Something like `C:\Users\Username\GCC`. Extract the 7z file using 7zip (https://www.7-zip.org/) and then move the directory into that GCC folder we created.
Later on, we will need to set up the environment variables so that other software knows where we installed GCC.
## Install SDL2
Download the "devel mingw" versions of the following SDL2 releases (again, ideally the latest versions, just providing links for examples).
* SDL2 https://github.com/libsdl-org/SDL/releases/download/release-2.24.0/SDL2-devel-2.24.0-mingw.zip
* SDL2_Image https://github.com/libsdl-org/SDL_image/releases/download/release-2.6.2/SDL2_image-devel-2.6.2-mingw.zip
* SDL2_TTF https://github.com/libsdl-org/SDL_ttf/releases/download/release-2.20.1/SDL2_ttf-devel-2.20.1-mingw.zip
* SDL2_Mixer https://github.com/libsdl-org/SDL_mixer/releases/download/release-2.6.2/SDL2_mixer-devel-2.6.2-mingw.zip
Extract the zip files (you can use 7zip or the built-in Windows zip tool) to get a directory for each. They should look something like `SDL2_ttf-2.20.1`.
Then put these all in some folder you will remember, something like `C:\Users\Username\SDL2`.
You should end up with 4 folders inside the `SDL2` folder you created, one for each of the things we downloaded.
## Set environment variables
Now that we have all the software downloaded, we need to set some environment variables so that everything will know where we installed the software.
Go to start menu, search for "env", and select "edit the system environment variables", and hit the `Environment Variables...` button in the bottom right.
Set the following env variables (change the username directory to match your username as needed).
First we need to set GCC as our default C compiler:
```
CC = gcc
``
Then we need to enable CGO (allowing the Go compiler to use a C compiler too):
```
CGO_ENABLED = 1
```
Then we need to set our "include" path. This is all of the directories that contain C header files to be included in our code. We need this to include the default GCC include directory as well as each of the SDL2 library include directories that we downloaded. This might be easier to copy-paste yourself if the versions are different. Also, change the `Username` to match your actual username (use search-replace to make this easier).
```
C_INCLUDE_PATH = C:\Users\Username\GCC\x86_64-12.1.0-release-win32-seh-rt_v10-rev3\mingw64\include;C:\Users\Username\SDL2\SDL2_image-2.6.2\x86_64-w64-mingw32\include;C:\Users\Username\SDL2\SDL2_mixer-2.6.2\x86_64-w64-mingw32\include;C:\Users\Username\SDL2\SDL2_ttf-2.20.1\x86_64-w64-mingw32\include;C:\Users\Username\SDL2\SDL2-2.24.0\x86_64-w64-mingw32\include;C:\Users\Username\SDL2\SDL2-2.24.0\x86_64-w64-mingw32\include\SDL2;
```
Then we need to set our "library" path. This is all of the directories that contain compiled library files (on Windows these are `*.dll` files) that can be imported into our code. This only needs to add all of the SDL2 directories.
```
LIBRARY_PATH = C:\Users\Username\SDL2\SDL2-2.24.0\x86_64-w64-mingw32\bin;C:\Users\Username\SDL2\SDL2_ttf-2.20.1\x86_64-w64-mingw32\bin;C:\Users\Username\SDL2\SDL2_mixer-2.6.2\x86_64-w64-mingw32\bin;C:\Users\Username\SDL2\SDL2_image-2.6.2\x86_64-w64-mingw32\bin;
```
And depending on how you installed GCC, you may need to also add its `bin` directory to your path so that you will be able to call the `gcc` compiler. There are probably other entries already in your `PATH` variable, so just add this as an additional entry:
```
PATH = C:\Users\Username\GCC\x86_64-12.1.0-release-win32-seh-rt_v10-rev3\mingw64\bin
```
## Copy the SDL2 libraries into your code directory
Your code may compile successfully, but it might not run because of the missing SDL2 libraries. Copy each of the `*.dll` library files into your code directory so that your resulting binary can load them correctly.
The DLL files should be in each of the `bin` directories of the SDL2 libraries that you downloaded.
## Build the software
Now we can just build the software and hope it works!
```
go build
```

View File

@ -0,0 +1,62 @@
# Go with SDL on everything else
Most any other operating system is Unix-like (e.g. Linux, Mac, BSD, Haiku, etc.), so they will all have very similar setups.
Each of these systems also has their own way of installing software, usually with their own package managers. I don't know all the exact names of each of these packages, but hopefully giving you some examples will help.
## Install the dependencies
You will need the following dependencies:
* Git
* Go (Golang)
* GCC
* SDL2 (with Image, TTF, and Mixer)
That's all, you may even have most of them installed already.
### Debian
As the most common example, on Debian or Ubuntu, the following `apt` command should install all the dependencies:
```sh
sudo apt install git golang gcc libsdl2-dev libsdl2-mixer-dev libsdl2-image-dev libsdl2-ttf-dev
```
The default version of Go in the Debian repos might be too old. You can install a newer version using the testing/unstable repos (try [this][3] or [this][4]), or you could [manually install Go from their website][2].
### Mac
On Mac, the typical recommendation is to use [Homebrew][5]. The package names are pretty similar:
```sh
brew install git go gcc sdl2 sdl2_image sdl2_ttf sdl2_mixer
```
### Redhat/Fedora/Rocky/CentOS
The Redhat-related distros all have very similar names as well, notably using `devel` instead of `dev` and `SDL2` is capitalized. I ran the following on Fedora successfully.
(these distros may use `dnf` or `yum` as the package manager executable, `dnf` is newer)
```sh
sudo dnf install git golang gcc SDL2-devel SDL2_image-devel SDL2_mixer-devel SDL2_ttf-devel
```
### pkgsrc
[pkgsrc][1] is NetBSD's package manager, but I've taken a liking to it and actually use it on Linux as well.
As a reference for the BSD crowd, the dependencies in pkgsrc are:
* devel/git
* lang/go119
* lang/gcc12
* devel/SDL2
* graphics/SDL2_image
* fonts/SDL2_ttf
* audio/SDL2_mixer
### Others
I apologize for not adding more systems, but at some point this list gets annoyingly long to repeat the same thing. Your distribution should almost certainly have these dependencies, and they will be named something similar to the above examples.
[1]: http://pkgsrc.org/
[2]: https://go.dev/
[3]: https://unix.stackexchange.com/q/8049
[4]: https://unix.stackexchange.com/q/107689
[5]: https://brew.sh/

4
game.conf Normal file
View File

@ -0,0 +1,4 @@
game.title = "Project Ely"
log.file = "output.log"
log.writeToFile = false

5
go.mod
View File

@ -1,3 +1,8 @@
module gitea.wisellama.rocks/Project-Ely/project-ely
go 1.18
require (
gitea.wisellama.rocks/Wisellama/gosimpleconf v0.0.4
github.com/veandco/go-sdl2 v0.4.25
)

6
go.sum Normal file
View File

@ -0,0 +1,6 @@
gitea.wisellama.rocks/Wisellama/gosimpleconf v0.0.3 h1:g4hcc7TjodjMNrSEQ3UO96HXvltHc2EOiudS+Wfle60=
gitea.wisellama.rocks/Wisellama/gosimpleconf v0.0.3/go.mod h1:kY9gQL8laVTe+tW0ue5bYb6QThw78d7mx6AHwQ5CIzc=
gitea.wisellama.rocks/Wisellama/gosimpleconf v0.0.4 h1:xFjG/dGPUoh1L7/PG9xQRr0GQZwlh1EGI9RnG8Ja8R8=
gitea.wisellama.rocks/Wisellama/gosimpleconf v0.0.4/go.mod h1:kY9gQL8laVTe+tW0ue5bYb6QThw78d7mx6AHwQ5CIzc=
github.com/veandco/go-sdl2 v0.4.25 h1:J5ac3KKOccp/0xGJA1PaNYKPUcZm19IxhDGs8lJofPI=
github.com/veandco/go-sdl2 v0.4.25/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY=

137
main.go
View File

@ -3,9 +3,142 @@ package main
import (
"fmt"
"log"
"os"
"runtime"
"gitea.wisellama.rocks/Project-Ely/project-ely/config"
"gitea.wisellama.rocks/Wisellama/gosimpleconf"
"github.com/veandco/go-sdl2/sdl"
)
func main() {
fmt.Println("Soup")
log.Printf("logs\n")
var err error
configMap, err := config.Configure("game.conf")
if err != nil {
log.Printf("error during configure: %v\n", err)
os.Exit(1)
}
err = run(configMap)
if err != nil {
log.Fatal(err)
}
}
func run(configMap gosimpleconf.ConfigMap) error {
runtime.LockOSThread()
err := sdl.Init(config.SDL_INIT_FLAGS)
if err != nil {
err = fmt.Errorf("failed initializing SDL: %w", err)
return err
}
defer sdl.Quit()
err = config.SdlSettings()
if err != nil {
err = fmt.Errorf("failed in SDL settings: %w", err)
return err
}
gameWindow, err := createWindow(configMap["game.title"])
if err != nil {
err = fmt.Errorf("failed creating SDL window: %w", err)
return err
}
defer gameWindow.Destroy()
renderer, err := sdl.CreateRenderer(gameWindow, -1, sdl.RENDERER_ACCELERATED)
if err != nil {
err = fmt.Errorf("failed creating SDL renderer: %w", err)
return err
}
defer renderer.Destroy()
var rect *sdl.Rect
var x int32 = 0
var y int32 = 0
keystates := make(map[sdl.Keycode]bool)
running := true
for running {
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
switch e := event.(type) {
case *sdl.QuitEvent:
running = false
case *sdl.KeyboardEvent:
if e.Keysym.Sym == sdl.K_ESCAPE {
log.Println("Esc quitting")
running = false
}
// Key states (just set a boolean whether the key is actively being pressed)
if e.Type == sdl.KEYDOWN {
keystates[e.Keysym.Sym] = true
} else if e.Type == sdl.KEYUP {
keystates[e.Keysym.Sym] = false
}
}
}
if keystates[sdl.K_a] {
x -= 1
if x < 0 {
x = 0
}
}
if keystates[sdl.K_d] {
x += 1
w, _ := gameWindow.GetSize()
if x+rect.W > w {
x = w - rect.W
}
}
// Y is inverted, positive Y moves downward
if keystates[sdl.K_w] {
y -= 1
if y < 0 {
y = 0
}
}
if keystates[sdl.K_s] {
y += 1
_, h := gameWindow.GetSize()
if y+rect.H > h {
y = h - rect.H
}
}
// Black background
renderer.SetDrawColor(0, 0, 0, 255)
renderer.Clear()
// Blue square
renderer.SetDrawColor(0, 0, 255, 255)
rect = &sdl.Rect{X: x, Y: y, W: 200, H: 200}
renderer.DrawRect(rect)
renderer.FillRect(rect)
renderer.Present()
sdl.Delay(16)
}
return nil
}
func createWindow(title string) (*sdl.Window, error) {
window, err := sdl.CreateWindow(
title,
sdl.WINDOWPOS_UNDEFINED,
sdl.WINDOWPOS_UNDEFINED,
config.SDL_WINDOW_WIDTH,
config.SDL_WINDOW_HEIGHT,
config.SDL_WINDOW_FLAGS)
if err != nil {
log.Println("Failed creating SDL window")
return nil, err
}
return window, nil
}