Skip to content

Commit 16826ba

Browse files
authored
Merge pull request #26 from zigster64/garden2
Garden2
2 parents b0aa764 + 0e79bcc commit 16826ba

129 files changed

Lines changed: 801 additions & 42 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

build.zig

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub fn build(b: *std.Build) void {
3838
.{ .file = "examples/01_basic.zig", .name = "example_1" },
3939
.{ .file = "examples/02_petshop.zig", .name = "example_2" },
4040
.{ .file = "examples/022_petshop.zig", .name = "example_22" },
41-
// .{ .file = "examples/03_racing.zig", .name = "example_3" },
41+
.{ .file = "examples/05_garden.zig", .name = "example_5" },
4242
};
4343

4444
{
@@ -59,9 +59,6 @@ pub fn build(b: *std.Build) void {
5959
const logz_module = b.dependency("logz", dep_opts);
6060
exe.root_module.addImport("logz", logz_module.module("logz"));
6161

62-
const zts_module = b.dependency("zts", dep_opts);
63-
exe.root_module.addImport("zts", zts_module.module("zts"));
64-
6562
b.installArtifact(exe);
6663

6764
const run_cmd = b.addRunArtifact(exe);

build.zig.zon

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,15 @@
66
.dependencies = .{
77
.httpz = .{
88
// using the dev branch so we are on zig 0.15.dev
9+
// Im still using version 855, later versions of zig dev (and http.zig dev) are so out of whack
10+
// due to writergate and other stdlib changes, that Im pinning things to this stable commit here :)
911
.url = "git+https://github.com/karlseguin/http.zig?ref=dev#aa65ee07b8ea47b7e3d6105c3135630d372d0fea",
1012
.hash = "httpz-0.0.0-PNVzrAvDBgCyEuvLZqN-dPEudxSOs0mqYg3hP7TGa3Cx",
1113
},
1214
.logz = .{
1315
.url = "git+https://github.com/karlseguin/log.zig#db6ec9818cc6dabf853c3271d8fecb7af5d4755c",
1416
.hash = "logz-0.0.0-O9YWXlhHAgD8srw1WqQ2SPde3T674hoD3mGFPCVCWFPS",
1517
},
16-
.zts = .{
17-
.url = "git+https://github.com/zigster64/zts.git#a58ea25255232ff04f63cfb25fd44a5494ef1a84",
18-
.hash = "zts-0.14.1-AAAAABazAgAWx8_iVDidfnwfB9M4EtnVzUfqgdmw6qjU",
19-
},
2018
},
2119
.paths = .{
2220
"build.zig",

examples/022_cats.zig

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
const std = @import("std");
22
const httpz = @import("httpz");
33
const logz = @import("logz");
4-
const zts = @import("zts");
54
const datastar = @import("datastar");
65
const Allocator = std.mem.Allocator;
76

@@ -97,10 +96,6 @@ pub const App = struct {
9796
return s;
9897
}
9998

100-
pub fn enableSubscriptions(app: *App) !void {
101-
app.subscribers = try datastar.Subscribers(*App).init(app.gpa, app);
102-
}
103-
10499
pub fn deinit(app: *App) void {
105100
app.streams.deinit();
106101
app.cats.deinit();
@@ -161,11 +156,9 @@ pub const App = struct {
161156
const t1 = std.time.microTimestamp();
162157
defer {
163158
const t2 = std.time.microTimestamp();
164-
logz.info().string("event", "publishCatList").int("elapsed (μs)", t2 - t1).log();
159+
logz.info().string("event", "publishCatList").int("stream", stream.handle).string("session", session.?).int("elapsed (μs)", t2 - t1).log();
165160
}
166161

167-
std.debug.print("publishCatList with session {?s}\n", .{session});
168-
169162
// Update the HTML in the correct order
170163
var msg = datastar.patchElementsOpt(stream, .{ .view_transition = true });
171164
defer msg.end();

examples/022_petshop.zig

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
const std = @import("std");
22
const httpz = @import("httpz");
33
const logz = @import("logz");
4-
const zts = @import("zts");
54
const datastar = @import("datastar");
65
const App = @import("022_cats.zig").App;
76
const SortType = @import("022_cats.zig").SortType;
@@ -16,8 +15,7 @@ pub fn main() !void {
1615
var gpa = std.heap.DebugAllocator(.{}).init;
1716
const allocator = gpa.allocator();
1817

19-
var app = try App.init(allocator);
20-
try app.enableSubscriptions();
18+
const app = try App.init(allocator);
2119

2220
var server = try httpz.Server(*App).init(allocator, .{
2321
.port = PORT,

examples/02_cats.zig

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
const std = @import("std");
22
const httpz = @import("httpz");
33
const logz = @import("logz");
4-
const zts = @import("zts");
54
const datastar = @import("datastar");
65
const Allocator = std.mem.Allocator;
76

@@ -75,7 +74,7 @@ pub const App = struct {
7574
const t1 = std.time.microTimestamp();
7675
defer {
7776
const t2 = std.time.microTimestamp();
78-
logz.info().string("event", "publishCatList").int("elapsed (μs)", t2 - t1).log();
77+
logz.info().string("event", "publishCatList").int("stream", stream.handle).int("elapsed (μs)", t2 - t1).log();
7978
}
8079

8180
// Update the HTML in the correct order

examples/02_petshop.zig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
const std = @import("std");
22
const httpz = @import("httpz");
33
const logz = @import("logz");
4-
const zts = @import("zts");
54
const datastar = @import("datastar");
65
const App = @import("02_cats.zig").App;
76

examples/05_garden.zig

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
const std = @import("std");
2+
const httpz = @import("httpz");
3+
const logz = @import("logz");
4+
const datastar = @import("datastar");
5+
const plants = @import("05_plants.zig");
6+
const homepage = @embedFile("05_index.html");
7+
8+
const App = plants.App;
9+
const Allocator = std.mem.Allocator;
10+
11+
const PORT = 8085;
12+
13+
// SSE and pub/sub to have realtime updates of updates to the garden
14+
pub fn main() !void {
15+
var gpa: std.heap.DebugAllocator(.{}) = .init;
16+
const allocator = gpa.allocator();
17+
18+
var app = try App.init(allocator);
19+
defer app.deinit();
20+
21+
var server = try httpz.Server(*App).init(allocator, .{
22+
.port = PORT,
23+
.address = "0.0.0.0",
24+
}, app);
25+
26+
const game_t = try std.Thread.spawn(.{}, updateLoop, .{app});
27+
defer game_t.join();
28+
29+
defer { // clean shutdown
30+
server.stop();
31+
server.deinit();
32+
}
33+
34+
// initialize a logging pool
35+
try logz.setup(allocator, .{
36+
.level = .Info,
37+
.pool_size = 100,
38+
.buffer_size = 4096,
39+
.large_buffer_count = 8,
40+
.large_buffer_size = 16384,
41+
.output = .stdout,
42+
.encoding = .logfmt,
43+
});
44+
defer logz.deinit();
45+
46+
var router = try server.router(.{});
47+
48+
router.get("/", index, .{});
49+
router.get("/plants", plantList, .{});
50+
router.post("/planteffect/:side/:plantid", postPlantEffect, .{});
51+
router.get("/assets/:assetname", postAsset, .{});
52+
53+
std.debug.print("listening http://localhost:{d}/\n", .{PORT});
54+
std.debug.print("... or any other IP address pointing to this machine\n", .{});
55+
try server.listen();
56+
}
57+
58+
fn updateLoop(app: *App) !void {
59+
while (true) {
60+
try app.updatePlants();
61+
std.Thread.sleep(std.time.ns_per_s);
62+
}
63+
}
64+
65+
fn index(_: *App, _: *httpz.Request, res: *httpz.Response) !void {
66+
const t1 = std.time.microTimestamp();
67+
defer {
68+
const t2 = std.time.microTimestamp();
69+
logz.info().string("event", "index").int("elapsed (μs)", t2 - t1).log();
70+
}
71+
res.content_type = .HTML;
72+
res.body = homepage;
73+
}
74+
75+
fn postAsset(_: *App, req: *httpz.Request, res: *httpz.Response) !void {
76+
const t1 = std.time.microTimestamp();
77+
defer {
78+
const t2 = std.time.microTimestamp();
79+
logz.info().string("event", "index").int("elapsed (μs)", t2 - t1).log();
80+
}
81+
const file_name = req.param("assetname").?;
82+
83+
const static_dir = "./examples/assets/fantasy_crops"; //try std.fmt.allocPrint(res.arena, "{s}/{s}", .{static_dir,file_name});
84+
const fullPath = try std.fmt.allocPrint(res.arena, "{s}/{s}", .{ static_dir, file_name });
85+
86+
const file = try std.fs.cwd().openFile(fullPath, .{});
87+
defer file.close();
88+
res.content_type = .PNG;
89+
res.body = try file.readToEndAlloc(res.arena, 100000);
90+
}
91+
92+
fn plantList(app: *App, _: *httpz.Request, res: *httpz.Response) !void {
93+
const t1 = std.time.microTimestamp();
94+
app.mutex.lock();
95+
defer {
96+
app.mutex.unlock();
97+
const t2 = std.time.microTimestamp();
98+
logz.info().string("event", "plantsList").int("elapsed (μs)", t2 - t1).log();
99+
}
100+
101+
const stream: std.net.Stream = try res.startEventStreamSync();
102+
// DO NOT close - this stream stays open forever
103+
// and gets subscribed to "plants" update events
104+
try app.subscribe("plants", stream, App.publishPlantList);
105+
try app.subscribe("crops", stream, App.publishCropCounts);
106+
107+
// just to be silly - try to multi subscribe, and observe that it detects that its already subbed
108+
try app.subscribe("plants", stream, App.publishPlantList);
109+
try app.subscribe("crops", stream, App.publishCropCounts);
110+
try app.subscribe("plants", stream, App.publishPlantList);
111+
try app.subscribe("crops", stream, App.publishCropCounts);
112+
}
113+
114+
fn postPlantEffect(app: *App, req: *httpz.Request, _: *httpz.Response) !void {
115+
const t1 = std.time.microTimestamp();
116+
app.mutex.lock();
117+
defer {
118+
app.mutex.unlock();
119+
const t2 = std.time.microTimestamp();
120+
logz.info().string("event", "postPlantEffect").int("elapsed (μs)", t2 - t1).log();
121+
}
122+
123+
const side_param = req.param("side").?;
124+
const left = if (std.mem.eql(u8, side_param, "inc")) true else false;
125+
126+
const id_param = req.param("plantid").?;
127+
const id = try std.fmt.parseInt(usize, id_param, 10);
128+
129+
if (id < 0 or id >= 4) return error.InvalidID;
130+
131+
const Hand = struct {
132+
hand: []const u8,
133+
};
134+
135+
const signals = try datastar.readSignals(Hand, req);
136+
137+
var plant_slot = app.plants[id];
138+
if (plant_slot) |*plant| { // Plant exists
139+
if (plant.growth_stage == .Fruiting) { //Collect crop and update crop counter
140+
switch (plant.crop_type) {
141+
.Carrot => {
142+
app.crop_counts[0] += 1;
143+
},
144+
.Radish => {
145+
app.crop_counts[1] += 1;
146+
},
147+
.Gourd => {
148+
app.crop_counts[2] += 1;
149+
},
150+
.Onion => {
151+
app.crop_counts[3] += 1;
152+
},
153+
}
154+
app.plants[id] = null;
155+
try app.publish("plants");
156+
try app.publish("crops");
157+
return;
158+
}
159+
160+
if (std.mem.eql(u8, signals.hand, "watering")) {
161+
app.plants[id].?.stats.water += if (left) 0.1 else 0;
162+
} else if (std.mem.eql(u8, signals.hand, "fertilizing")) {
163+
app.plants[id].?.stats.ph += if (left) 0.1 else -0.1;
164+
} else if (std.mem.eql(u8, signals.hand, "sunning")) {
165+
app.plants[id].?.stats.sun += if (left) 0.1 else -0.1;
166+
} else if (std.mem.eql(u8, signals.hand, "shovel")) {
167+
// Remove plant at index
168+
app.plants[id] = null;
169+
}
170+
} else {
171+
if (std.mem.eql(u8, signals.hand, "carrot")) {
172+
app.plants[id] = plants.CarrotConfig;
173+
} else if (std.mem.eql(u8, signals.hand, "gourd")) {
174+
app.plants[id] = plants.GourdConfig;
175+
} else if (std.mem.eql(u8, signals.hand, "radish")) {
176+
app.plants[id] = plants.RadishConfig;
177+
} else if (std.mem.eql(u8, signals.hand, "onion")) {
178+
app.plants[id] = plants.OnionConfig;
179+
}
180+
}
181+
// update any screens subscribed to "plants"
182+
try app.publish("plants");
183+
}

0 commit comments

Comments
 (0)