-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMakefile
More file actions
154 lines (132 loc) · 5.37 KB
/
Copy pathMakefile
File metadata and controls
154 lines (132 loc) · 5.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# Deadworks plugin dev loop with hot-reload.
#
# make dev MODE=deathmatch # build mode's plugins, start server, watch + hot-reload
# make dev MODE=trooper-invasion PORT=27018
#
# `make dev` builds every plugin in the chosen game mode (from gamemodes.json)
# into ./.dev-plugins, starts the Deadworks server (built from ../deadworks) with
# that folder bind-mounted, then watches each plugin's source. Editing and saving
# any .cs file rebuilds that plugin and the running server hot-reloads it — no
# restart. Ctrl-C stops the watchers and the server.
#
# Requirements: Docker + Docker Compose, the .NET 10 SDK, jq, and a sibling
# ../deadworks checkout. Copy .env.example to .env and fill in STEAM_LOGIN /
# STEAM_PASSWORD before the first run.
SHELL := /bin/bash
.SHELLFLAGS := -eu -o pipefail -c
.ONESHELL:
MODE ?=
PORT ?= 27015
LIVE_DIR := .dev-plugins
LIVE_ABS := $(abspath $(LIVE_DIR))
COMPOSE := docker-compose.dev.yml
DEADWORKS := ../deadworks
GAMEMODES := gamemodes.json
NOOP := .staging/deploy-noop
DC := MODE='$(MODE)' PORT='$(PORT)' docker compose -f $(COMPOSE)
# Use MSBuild's /p: form (not -p:): `dotnet watch` parses -p as --project and
# rejects two of them; /p: tokens are forwarded straight to the build.
PUBLISH_ARGS := --nologo /p:NoWarn=CS1591 /p:DeadlockManagedDir='$(abspath $(NOOP))'
.DEFAULT_GOAL := help
.PHONY: help list dev build watch up down stop logs clean check-env
help: ## Show this help
@echo "Deadworks plugin dev loop"
@echo
@echo "Usage: make <target> MODE=<game-mode> [PORT=<port>]"
@echo
@echo "Targets:"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) \
| awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-10s\033[0m %s\n", $$1, $$2}'
@echo
@echo "Game modes (from $(GAMEMODES)):"
@jq -r 'keys[] | " " + .' $(GAMEMODES)
list: ## List available game modes and their plugins
@jq -r 'to_entries[] | "\(.key):\n " + (.value | join("\n "))' $(GAMEMODES)
# Resolve + validate the plugin list for MODE; fail with a helpful message.
define require_mode
if [ -z '$(MODE)' ]; then
echo "error: set MODE=<game-mode>. Available:" >&2
jq -r 'keys[] | " " + .' $(GAMEMODES) >&2
exit 2
fi
plugins=$$(jq -r '.["$(MODE)"][]?' $(GAMEMODES))
if [ -z "$$plugins" ]; then
echo "error: unknown mode '$(MODE)'. Available:" >&2
jq -r 'keys[] | " " + .' $(GAMEMODES) >&2
exit 2
fi
endef
# Map a plugin folder name to its .csproj (skips folders without one).
define csproj_for
$$(ls "$1"/*.csproj 2>/dev/null | head -1)
endef
check-env:
@if [ ! -f .env ]; then
echo "error: .env not found — copy .env.example to .env and set STEAM_LOGIN / STEAM_PASSWORD" >&2
exit 2
fi
@if [ ! -d '$(DEADWORKS)' ]; then
echo "error: sibling deadworks checkout not found at '$(DEADWORKS)'" >&2
exit 2
fi
build: ## Build a mode's plugins once into ./.dev-plugins (no watch)
@$(require_mode)
rm -rf '$(LIVE_DIR)'; mkdir -p '$(LIVE_DIR)' # only this mode's plugins
for p in $$plugins; do
csproj=$(call csproj_for,$$p)
if [ -z "$$csproj" ]; then echo " (skip $$p: no .csproj)"; continue; fi
echo "==> building $$p"
dotnet publish "$$csproj" -o '$(LIVE_DIR)' $(PUBLISH_ARGS)
done
dev: check-env ## Start the server and hot-reload all of MODE's plugins (Ctrl-C to stop)
@$(require_mode)
rm -rf '$(LIVE_DIR)'; mkdir -p '$(LIVE_DIR)' # only this mode's plugins load
# 1. Warm-up build (serial) so the shared DeadworksManaged.Api builds once —
# avoids parallel build races between the per-plugin watchers below.
for p in $$plugins; do
csproj=$(call csproj_for,$$p)
if [ -z "$$csproj" ]; then echo " (skip $$p: no .csproj)"; continue; fi
echo "==> building $$p"
dotnet publish "$$csproj" -o '$(LIVE_DIR)' $(PUBLISH_ARGS)
done
# 2. Start (or rebuild) the dev server, detached.
echo "==> starting server for mode '$(MODE)' on port $(PORT)"
$(DC) up -d --build
# 3. Tear everything down cleanly on Ctrl-C / exit.
cleanup() { echo; echo "==> stopping"; kill $$(jobs -p) 2>/dev/null || true; $(DC) stop; }
trap cleanup EXIT INT TERM
# 4. One file-watcher per plugin, each re-publishing into the live folder.
# cd into the plugin dir (watch auto-detects the single csproj there);
# `dotnet watch --project` leaks the flag through to MSBuild on this SDK.
echo "==> watching $$(echo $$plugins | wc -w) plugin(s) — edit + save to hot-reload"
for p in $$plugins; do
csproj=$(call csproj_for,$$p)
[ -z "$$csproj" ] && continue
( cd "$$p" && exec dotnet watch --non-interactive publish -o '$(LIVE_ABS)' $(PUBLISH_ARGS) ) &
done
# 5. Follow server logs in the foreground; wait blocks until Ctrl-C.
$(DC) logs -f &
wait
watch: ## Watch + hot-reload MODE's plugins (assumes the server is already up)
@$(require_mode)
mkdir -p '$(LIVE_DIR)'
trap 'kill $$(jobs -p) 2>/dev/null || true' EXIT INT TERM
for p in $$plugins; do
csproj=$(call csproj_for,$$p)
[ -z "$$csproj" ] && continue
echo "==> watching $$p"
( cd "$$p" && exec dotnet watch --non-interactive publish -o '$(LIVE_ABS)' $(PUBLISH_ARGS) ) &
done
wait
up: check-env ## Start the dev server detached (no plugin building)
@$(require_mode)
$(DC) up -d --build
down: ## Stop and remove the dev server container
@$(DC) down
stop: ## Stop the dev server container (keep it for a fast restart)
@$(DC) stop
logs: ## Follow the dev server logs
@$(DC) logs -f
clean: ## Remove the live plugins folder
@rm -rf '$(LIVE_DIR)' '$(NOOP)'
@echo "removed $(LIVE_DIR)"