Add the chat gui and set the default text color to white.

This commit is contained in:
IntegratedQuantum 2023-03-22 13:59:46 +01:00
parent d5389aefb7
commit 77610c0064
14 changed files with 306 additions and 28 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -30,13 +30,13 @@ fn fileToString(allocator: Allocator, path: []const u8) ![]u8 {
}
pub const draw = struct {
var color: i32 = 0;
var color: u32 = 0;
var clip: ?Vec4i = null;
var translation: Vec2f = Vec2f{0, 0};
var scale: f32 = 1;
pub fn setColor(newColor: u32) void {
color = @bitCast(i32, newColor);
color = newColor;
}
/// Returns the previous translation.
@ -52,6 +52,7 @@ pub const draw = struct {
/// Returns the previous translation.
pub fn setScale(newScale: f32) f32 {
std.debug.assert(newScale >= 0);
const oldScale = scale;
scale *= newScale;
return oldScale;
@ -63,11 +64,12 @@ pub const draw = struct {
/// Returns the previous clip.
pub fn setClip(clipRect: Vec2f) ?Vec4i {
std.debug.assert(@reduce(.And, clipRect >= Vec2f{0, 0}));
var newClip = Vec4i {
@floatToInt(i32, translation[0]),
main.Window.height - @floatToInt(i32, translation[1] + clipRect[1]*scale),
@floatToInt(i32, clipRect[0]*scale),
@floatToInt(i32, clipRect[1]*scale),
std.math.lossyCast(i32, translation[0]),
main.Window.height - std.math.lossyCast(i32, translation[1] + clipRect[1]*scale),
std.math.lossyCast(i32, clipRect[0]*scale),
std.math.lossyCast(i32, clipRect[1]*scale),
};
if(clip) |oldClip| {
if (newClip[0] < oldClip[0]) {
@ -84,6 +86,8 @@ pub const draw = struct {
if (newClip[1] + newClip[3] > oldClip[1] + oldClip[3]) {
newClip[3] -= (newClip[1] + newClip[3]) - (oldClip[1] + oldClip[3]);
}
newClip[2] = @max(newClip[2], 0);
newClip[3] = @max(newClip[3], 0);
} else {
c.glEnable(c.GL_SCISSOR_TEST);
}
@ -151,7 +155,7 @@ pub const draw = struct {
c.glUniform2f(rectUniforms.screen, @intToFloat(f32, Window.width), @intToFloat(f32, Window.height));
c.glUniform2f(rectUniforms.start, pos[0], pos[1]);
c.glUniform2f(rectUniforms.size, dim[0], dim[1]);
c.glUniform1i(rectUniforms.rectColor, color);
c.glUniform1i(rectUniforms.rectColor, @bitCast(i32, color));
c.glBindVertexArray(rectVAO);
c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4);
@ -204,7 +208,7 @@ pub const draw = struct {
c.glUniform2f(lineUniforms.screen, @intToFloat(f32, Window.width), @intToFloat(f32, Window.height));
c.glUniform2f(lineUniforms.start, pos1[0], pos1[1]);
c.glUniform2f(lineUniforms.direction, pos2[0] - pos1[0], pos2[1] - pos1[1]);
c.glUniform1i(lineUniforms.lineColor, color);
c.glUniform1i(lineUniforms.lineColor, @bitCast(i32, color));
c.glBindVertexArray(lineVAO);
c.glDrawArrays(c.GL_LINE_STRIP, 0, 2);
@ -250,7 +254,7 @@ pub const draw = struct {
c.glUniform2f(lineUniforms.screen, @intToFloat(f32, Window.width), @intToFloat(f32, Window.height));
c.glUniform2f(lineUniforms.start, pos[0], pos[1]); // Move the coordinates, so they are in the center of a pixel.
c.glUniform2f(lineUniforms.direction, dim[0] - 1, dim[1] - 1); // The height is a lot smaller because the inner edge of the rect is drawn.
c.glUniform1i(lineUniforms.lineColor, color);
c.glUniform1i(lineUniforms.lineColor, @bitCast(i32, color));
c.glBindVertexArray(lineVAO);
c.glDrawArrays(c.GL_LINE_LOOP, 0, 5);
@ -303,7 +307,7 @@ pub const draw = struct {
c.glUniform2f(circleUniforms.screen, @intToFloat(f32, Window.width), @intToFloat(f32, Window.height));
c.glUniform2f(circleUniforms.center, center[0], center[1]); // Move the coordinates, so they are in the center of a pixel.
c.glUniform1f(circleUniforms.radius, radius); // The height is a lot smaller because the inner edge of the rect is drawn.
c.glUniform1i(circleUniforms.circleColor, color);
c.glUniform1i(circleUniforms.circleColor, @bitCast(i32, color));
c.glBindVertexArray(circleVAO);
c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4);
@ -341,7 +345,7 @@ pub const draw = struct {
c.glUniform2f(imageUniforms.screen, @intToFloat(f32, Window.width), @intToFloat(f32, Window.height));
c.glUniform2f(imageUniforms.start, pos[0], pos[1]);
c.glUniform2f(imageUniforms.size, dim[0], dim[1]);
c.glUniform1i(imageUniforms.color, color);
c.glUniform1i(imageUniforms.color, @bitCast(i32, color));
c.glBindVertexArray(rectVAO);
c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4);
@ -359,7 +363,7 @@ pub const draw = struct {
c.glUniform2f(uniforms.screen, @intToFloat(f32, Window.width), @intToFloat(f32, Window.height));
c.glUniform2f(uniforms.start, pos[0], pos[1]);
c.glUniform2f(uniforms.size, dim[0], dim[1]);
c.glUniform1i(uniforms.color, color);
c.glUniform1i(uniforms.color, @bitCast(i32, color));
c.glUniform1f(uniforms.scale, scale);
c.glBindVertexArray(rectVAO);
@ -382,7 +386,7 @@ pub const TextBuffer = struct {
};
pub const FontEffect = packed struct(u28) {
color: u24 = 0,
color: u24 = 0xffffff,
bold: bool = false,
italic: bool = false,
underline: bool = false,
@ -741,6 +745,7 @@ pub const TextBuffer = struct {
TextRendering.shader.bind();
c.glUniform2f(TextRendering.uniforms.scene, @intToFloat(f32, main.Window.width), @intToFloat(f32, main.Window.height));
c.glUniform1f(TextRendering.uniforms.ratio, draw.scale);
c.glUniform1f(TextRendering.uniforms.alpha, @intToFloat(f32, draw.color >> 24) / 255.0);
c.glActiveTexture(c.GL_TEXTURE0);
c.glBindTexture(c.GL_TEXTURE_2D, TextRendering.glyphTexture[0]);
c.glBindVertexArray(draw.rectVAO);
@ -767,7 +772,7 @@ pub const TextBuffer = struct {
y = 0;
if(line.isUnderline) y += 15
else y += 8;
draw.setColor(line.color | @as(u32, 0xff000000));
draw.setColor(line.color | (@as(u32, 0xff000000) & draw.color));
for(lineWraps, 0..) |lineWrap, j| {
const lineStart = @max(0, line.start);
const lineEnd = @min(lineWrap, line.end);
@ -856,6 +861,7 @@ const TextRendering = struct {
const swap = glyphTexture[1];
glyphTexture[1] = glyphTexture[0];
glyphTexture[0] = swap;
c.glActiveTexture(c.GL_TEXTURE0);
c.glBindTexture(c.GL_TEXTURE_2D, glyphTexture[0]);
c.glTexImage2D(c.GL_TEXTURE_2D, 0, c.GL_R8, newWidth, textureHeight, 0, c.GL_RED, c.GL_UNSIGNED_BYTE, null);
c.glCopyImageSubData(
@ -1349,8 +1355,7 @@ pub const TextureArray = struct {
c.glTexSubImage3D(c.GL_TEXTURE_2D_ARRAY, lod, 0, 0, @intCast(c_int, i), curWidth, curHeight, 1, c.GL_RGBA, c.GL_UNSIGNED_BYTE, lodBuffer[lod].ptr);
}
}
c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAX_LOD, maxLOD);
c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_BASE_LEVEL, 5);
c.glTexParameteri(c.GL_TEXTURE_2D_ARRAY, c.GL_TEXTURE_MAX_LOD, maxLOD);
//glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
c.glTexParameteri(c.GL_TEXTURE_2D_ARRAY, c.GL_TEXTURE_MIN_FILTER, c.GL_NEAREST_MIPMAP_LINEAR);
c.glTexParameteri(c.GL_TEXTURE_2D_ARRAY, c.GL_TEXTURE_MAG_FILTER, c.GL_NEAREST);

View File

@ -63,6 +63,8 @@ isHud: bool = false,
/// Called every frame.
renderFn: *const fn()Allocator.Error!void = &defaultErrorFunction,
/// Called every frame before rendering.
updateFn: *const fn()Allocator.Error!void = &defaultErrorFunction,
/// Called every frame for the currently selected window.
updateSelectedFn: *const fn()Allocator.Error!void = &defaultErrorFunction,
/// Called every frame for the currently hovered window.
@ -310,6 +312,10 @@ fn positionRelativeToConnectedWindow(self: *GuiWindow, other: *GuiWindow, i: usi
}
}
pub fn update(self: *GuiWindow) !void {
try self.updateFn();
}
pub fn updateSelected(self: *GuiWindow, mousePosition: Vec2f) !void {
try self.updateSelectedFn();
const windowSize = main.Window.getWindowSize()/@splat(2, gui.scale);
@ -478,7 +484,7 @@ pub fn render(self: *const GuiWindow, mousePosition: Vec2f) !void {
draw.restoreTranslation(oldTranslation);
draw.restoreScale(oldScale);
if(self.showTitleBar) {
var text = try graphics.TextBuffer.init(gui.allocator, self.title, .{}, false, .center);
var text = try graphics.TextBuffer.init(gui.allocator, self.title, .{.color=0}, false, .center);
defer text.deinit();
const titleDimension = try text.calculateLineBreaks(16*self.scale, self.size[0]);
try text.render(self.pos[0] + self.size[0]/2 - titleDimension[0]/2, self.pos[1], 16*self.scale);

View File

@ -18,6 +18,7 @@ const fontSize: f32 = 16;
pos: Vec2f,
size: Vec2f,
text: TextBuffer,
alpha: f32 = 1,
pub fn init(pos: Vec2f, maxWidth: f32, text: []const u8, alignment: TextBuffer.Alignment) Allocator.Error!*Label {
const self = try gui.allocator.create(Label);
@ -49,5 +50,6 @@ pub fn updateText(self: *Label, newText: []const u8) !void {
}
pub fn render(self: *Label, _: Vec2f) !void {
draw.setColor(@floatToInt(u32, self.alpha*255) << 24);
try self.text.render(self.pos[0], self.pos[1], fontSize);
}

View File

@ -0,0 +1,79 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const main = @import("root");
const graphics = main.graphics;
const draw = graphics.draw;
const TextBuffer = graphics.TextBuffer;
const vec = main.vec;
const Vec2f = vec.Vec2f;
const gui = @import("../gui.zig");
const GuiComponent = gui.GuiComponent;
const ScrollBar = GuiComponent.ScrollBar;
const MutexComponent = @This();
const scrollBarWidth = 5;
const border: f32 = 3;
pos: Vec2f = undefined,
size: Vec2f = undefined,
child: GuiComponent = undefined,
mutex: std.Thread.Mutex = .{},
pub fn updateInner(self: *MutexComponent, _other: anytype) Allocator.Error!void {
std.debug.assert(!self.mutex.tryLock()); // self.mutex must be locked before calling!
var other: GuiComponent = undefined;
if(@TypeOf(_other) == GuiComponent) {
other = _other;
} else {
other = _other.toComponent();
}
self.child = other;
self.pos = other.pos();
self.size = other.size();
}
pub fn deinit(self: *MutexComponent) void {
std.debug.assert(!self.mutex.tryLock()); // self.mutex must be locked before calling!
self.child.deinit();
}
pub fn toComponent(self: *MutexComponent) GuiComponent {
return GuiComponent {
.mutexComponent = self
};
}
pub fn updateSelected(self: *MutexComponent) void {
self.mutex.lock();
defer self.mutex.unlock();
self.child.updateSelected();
}
pub fn updateHovered(self: *MutexComponent, mousePosition: Vec2f) void {
self.mutex.lock();
defer self.mutex.unlock();
self.child.updateHovered(mousePosition);
}
pub fn render(self: *MutexComponent, mousePosition: Vec2f) anyerror!void { // TODO: Remove anyerror once error union inference works in recursive loops.
self.mutex.lock();
defer self.mutex.unlock();
try self.child.render(mousePosition);
self.pos = self.child.pos();
self.size = self.child.size();
}
pub fn mainButtonPressed(self: *MutexComponent, mousePosition: Vec2f) void {
self.mutex.lock();
defer self.mutex.unlock();
self.child.mainButtonPressed(mousePosition);
}
pub fn mainButtonReleased(self: *MutexComponent, mousePosition: Vec2f) void {
self.mutex.lock();
defer self.mutex.unlock();
self.child.mainButtonReleased(mousePosition);
}

View File

@ -33,6 +33,7 @@ maxWidth: f32,
maxHeight: f32,
textSize: Vec2f = undefined,
scrollBar: *ScrollBar,
onNewline: ?*const fn() void,
pub fn __init() !void {
texture = try Texture.initFromFile("assets/cubyz/ui/text_input.png");
@ -42,7 +43,7 @@ pub fn __deinit() void {
texture.deinit();
}
pub fn init(pos: Vec2f, maxWidth: f32, maxHeight: f32, text: []const u8) Allocator.Error!*TextInput {
pub fn init(pos: Vec2f, maxWidth: f32, maxHeight: f32, text: []const u8, onNewline: ?*const fn() void) Allocator.Error!*TextInput {
const scrollBar = try ScrollBar.init(undefined, scrollBarWidth, maxHeight - 2*border, 0);
const self = try gui.allocator.create(TextInput);
self.* = TextInput {
@ -53,6 +54,7 @@ pub fn init(pos: Vec2f, maxWidth: f32, maxHeight: f32, text: []const u8) Allocat
.maxWidth = maxWidth,
.maxHeight = maxHeight,
.scrollBar = scrollBar,
.onNewline = onNewline,
};
try self.currentString.appendSlice(text);
self.textSize = try self.textBuffer.calculateLineBreaks(fontSize, maxWidth - 2*border - scrollBarWidth);
@ -66,6 +68,15 @@ pub fn deinit(self: *const TextInput) void {
gui.allocator.destroy(self);
}
pub fn clear(self: *TextInput) !void {
if(self.cursor != null) {
self.cursor = 0;
self.selectionStart = null;
}
self.currentString.clearRetainingCapacity();
try self.reloadText();
}
pub fn toComponent(self: *TextInput) GuiComponent {
return GuiComponent {
.textInput = self
@ -420,7 +431,11 @@ pub fn cut(self: *TextInput, mods: main.Key.Modifiers) void {
}
}
pub fn newline(self: *TextInput, _: main.Key.Modifiers) void {
pub fn newline(self: *TextInput, mods: main.Key.Modifiers) void {
if(!mods.shift) if(self.onNewline) |onNewline| {
onNewline();
return;
};
self.inputCharacter('\n') catch |err| {
std.log.err("Error while entering text: {s}", .{@errorName(err)});
};

View File

@ -17,7 +17,7 @@ const TextInput = @import("components/TextInput.zig");
pub const GuiComponent = @import("gui_component.zig").GuiComponent;
pub const GuiWindow = @import("GuiWindow.zig");
const windowlist = @import("windows/_windowlist.zig");
pub const windowlist = @import("windows/_windowlist.zig");
var windowList: std.ArrayList(*GuiWindow) = undefined;
var hudWindows: std.ArrayList(*GuiWindow) = undefined;
@ -402,6 +402,9 @@ pub fn updateAndRenderGui() !void {
break;
}
}
for(openWindows.items) |window| {
try window.update();
}
for(openWindows.items) |window| {
const oldScale = draw.setScale(scale);
defer draw.restoreScale(oldScale);

View File

@ -10,6 +10,7 @@ pub const GuiComponent = union(enum) {
pub const CheckBox = @import("components/CheckBox.zig");
pub const HorizontalList = @import("components/HorizontalList.zig");
pub const Label = @import("components/Label.zig");
pub const MutexComponent = @import("components/MutexComponent.zig");
pub const Slider = @import("components/Slider.zig");
pub const ScrollBar = @import("components/ScrollBar.zig");
pub const TextInput = @import("components/TextInput.zig");
@ -20,6 +21,7 @@ pub const GuiComponent = union(enum) {
checkBox: *CheckBox,
horizontalList: *HorizontalList,
label: *Label,
mutexComponent: *MutexComponent,
scrollBar: *ScrollBar,
slider: *Slider,
textInput: *TextInput,

View File

@ -1,5 +1,6 @@
pub const change_name = @import("change_name.zig");
pub const chat = @import("chat.zig");
pub const controls = @import("controls.zig");
pub const crosshair = @import("crosshair.zig");
pub const graphics = @import("graphics.zig");

View File

@ -49,13 +49,13 @@ pub fn onOpen() Allocator.Error!void {
if(settings.playerName.len == 0) {
try list.add(try Label.init(.{0, 0}, width, "Please enter your name!", .center));
} else {
try list.add(try Label.init(.{0, 0}, width, "#ff0000Warning: #000000You loose access to your inventory data when changing the name!", .center));
try list.add(try Label.init(.{0, 0}, width, "#ff0000Warning: #ffffffYou loose access to your inventory data when changing the name!", .center));
}
try list.add(try Label.init(.{0, 0}, width, "Cubyz supports formatting your username using a markdown-like syntax:", .center));
try list.add(try Label.init(.{0, 0}, width, "\\**italic*\\* \\*\\***bold**\\*\\* \\__underlined_\\_ \\_\\___strike-through__\\_\\_", .center));
try list.add(try Label.init(.{0, 0}, width, "Even colors are possible, using the hexadecimal color code:", .center));
try list.add(try Label.init(.{0, 0}, width, "\\##ff0000ff#00000000#00000000#ff0000red#000000 \\##ff0000ff#00770077#00000000#ff7700orange#000000 \\##00000000#00ff00ff#00000000#00ff00green#000000 \\##00000000#00000000#0000ffff#0000ffblue", .center));
textComponent = try TextInput.init(.{0, 0}, width, 32, "quanturmdoelvloper");
try list.add(try Label.init(.{0, 0}, width, "\\##ff0000ff#ffffff00#ffffff00#ff0000red#ffffff \\##ff0000ff#00770077#ffffff00#ff7700orange#ffffff \\##ffffff00#00ff00ff#ffffff00#00ff00green#ffffff \\##ffffff00#ffffff00#0000ffff#0000ffblue", .center));
textComponent = try TextInput.init(.{0, 0}, width, 32, "quanturmdoelvloper", &apply);
try list.add(textComponent);
try list.add(try Button.init(.{0, 0}, 100, "Apply", &apply));
list.finish(.center);

132
src/gui/windows/chat.zig Normal file
View File

@ -0,0 +1,132 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const main = @import("root");
const Vec2f = main.vec.Vec2f;
const gui = @import("../gui.zig");
const GuiComponent = gui.GuiComponent;
const GuiWindow = gui.GuiWindow;
const Button = @import("../components/Button.zig");
const Label = GuiComponent.Label;
const MutexComponent = GuiComponent.MutexComponent;
const TextInput = GuiComponent.TextInput;
const VerticalList = @import("../components/VerticalList.zig");
var components: [1]GuiComponent = undefined;
pub var window: GuiWindow = GuiWindow {
.contentSize = Vec2f{128, 256},
.id = "cubyz:chat",
.title = "Chat",
.onOpenFn = &onOpen,
.onCloseFn = &onClose,
.updateFn = &update,
.renderFn = &render,
.components = &components,
.showTitleBar = false,
.hasBackground = false,
.isHud = true,
};
const padding: f32 = 8;
const messageTimeout: i32 = 10000;
const messageFade: i32 = 1000;
var mutexComponent: MutexComponent = .{};
var history: std.ArrayList(*Label) = undefined;
var expirationTime: std.ArrayList(i32) = undefined;
var historyStart: u32 = 0;
var fadeOutEnd: u32 = 0;
var input: *TextInput = undefined;
var hideInput: bool = true;
fn refresh(deleteOld: bool) Allocator.Error!void {
std.debug.assert(!mutexComponent.mutex.tryLock()); // mutex must be locked!
if(deleteOld) {
components[0].mutexComponent.child.verticalList.children.clearRetainingCapacity();
components[0].deinit();
}
var list = try VerticalList.init(.{padding, 16 + padding}, 300, 0);
for(history.items[if(hideInput) historyStart else 0 ..]) |msg| {
msg.pos = .{0, 0};
try list.add(msg);
}
if(!hideInput) {
input.pos = .{0, 0};
try list.add(input);
}
list.finish(.center);
list.scrollBar.currentState = 1;
try mutexComponent.updateInner(list);
components[0] = mutexComponent.toComponent();
window.contentSize = components[0].pos() + components[0].size() + @splat(2, @as(f32, padding));
gui.updateWindowPositions();
}
pub fn onOpen() Allocator.Error!void {
history = std.ArrayList(*Label).init(main.globalAllocator);
expirationTime = std.ArrayList(i32).init(main.globalAllocator);
historyStart = 0;
input = try TextInput.init(.{0, 0}, 256, 32, "", &sendMessage);
mutexComponent.mutex.lock();
defer mutexComponent.mutex.unlock();
try refresh(false);
}
pub fn onClose() void {
mutexComponent.mutex.lock();
defer mutexComponent.mutex.unlock();
for(history.items) |label| {
label.deinit();
}
history.deinit();
expirationTime.deinit();
input.deinit();
components[0].mutexComponent.child.verticalList.children.clearRetainingCapacity();
components[0].deinit();
}
pub fn update() Allocator.Error!void {
mutexComponent.mutex.lock();
defer mutexComponent.mutex.unlock();
while(fadeOutEnd < history.items.len and @truncate(i32, std.time.milliTimestamp()) -% expirationTime.items[fadeOutEnd] >= 0) {
fadeOutEnd += 1;
}
for(expirationTime.items[historyStart..fadeOutEnd], history.items[historyStart..fadeOutEnd]) |time, label| {
if(@truncate(i32, std.time.milliTimestamp()) -% time >= messageFade) {
historyStart += 1;
hideInput = main.Window.grabbed;
try refresh(true);
} else {
label.alpha = 1.0 - @intToFloat(f32, @truncate(i32, std.time.milliTimestamp()) -% time)/@intToFloat(f32, messageFade);
}
}
if(hideInput != main.Window.grabbed) {
hideInput = main.Window.grabbed;
try refresh(true);
}
}
pub fn render() Allocator.Error!void {
if(!hideInput) {
main.graphics.draw.setColor(0x80000000);
main.graphics.draw.rect(.{0, 0}, window.contentSize);
}
}
pub fn addMessage(message: []const u8) Allocator.Error!void {
mutexComponent.mutex.lock();
defer mutexComponent.mutex.unlock();
try history.append(try Label.init(.{0, 0}, 256, message, .left));
try expirationTime.append(@truncate(i32, std.time.milliTimestamp()) +% messageTimeout);
try refresh(true);
}
pub fn sendMessage() void {
main.network.Protocols.chat.send(main.game.world.?.conn, input.currentString.items) catch |err| {
std.log.err("Got error while trying to send chat message: {s}", .{@errorName(err)});
};
input.clear() catch |err| {
std.log.err("Got error while trying to send chat message: {s}", .{@errorName(err)});
};
}

View File

@ -21,6 +21,7 @@ pub var window = GuiWindow {
.title = "Multiplayer",
.onOpenFn = &onOpen,
.onCloseFn = &onClose,
.updateFn = &update,
.components = &components,
};
@ -37,7 +38,7 @@ const width: f32 = 420;
fn flawedDiscoverIpAddress() !void {
connection = try ConnectionManager.init(12347, true); // TODO: default port
ipAddress = try std.fmt.allocPrint(main.globalAllocator, "{}", .{connection.?.externalAddress});
gotIpAddress.store(true, .Monotonic);
gotIpAddress.store(true, .Release);
}
fn discoverIpAddress() void {
@ -94,7 +95,7 @@ pub fn onOpen() Allocator.Error!void {
ipAddressLabel = try Label.init(.{0, 0}, width, " ", .center);
try list.add(ipAddressLabel);
try list.add(try Button.init(.{0, 0}, 100, "Copy IP", &copyIp));
try list.add(try TextInput.init(.{0, 0}, width, 32, settings.lastUsedIPAddress));
try list.add(try TextInput.init(.{0, 0}, width, 32, settings.lastUsedIPAddress, &join));
try list.add(try Button.init(.{0, 0}, 100, "Join", &join));
list.finish(.center);
components[0] = list.toComponent();
@ -127,8 +128,8 @@ pub fn onClose() void {
}
}
pub fn render() void {
if(gotIpAddress.load(.Monotonic)) {
pub fn update() Allocator.Error!void {
if(gotIpAddress.load(.Acquire)) {
gotIpAddress.store(false, .Monotonic);
try ipAddressLabel.updateText(ipAddress);
}

View File

@ -1,6 +1,6 @@
const std = @import("std");
const gui = @import("gui");
pub const gui = @import("gui");
pub const assets = @import("assets.zig");
pub const blocks = @import("blocks.zig");

View File

@ -1201,6 +1201,38 @@ pub const Protocols: struct {
// addHeaderAndSendUnimportant(user, TIME_AND_BIOME, data.toString().getBytes(StandardCharsets.UTF_8));
// }
},
chat: type = struct {
const id: u8 = 10;
fn receive(conn: *Connection, data: []const u8) !void {
_ = conn;
// TODO:
// if(conn instanceof User) {
// User user = (User)conn;
// if(msg.startsWith("/")) {
// CommandExecutor.execute(msg, user);
// } else {
// msg = "["+user.name+"#ffffff] "+msg;
// sendToClients(msg);
// }
// } else {
try main.gui.windowlist.chat.addMessage(data);
// }
}
pub fn send(conn: *Connection, data: []const u8) !void {
try conn.sendImportant(id, data);
}
// TODO
// public void sendToClients(String msg) {
// Logger.log("chat", msg, "\033[0;32m");
// synchronized(this) {
// for(User user : Server.users) {
// send(user, msg);
// }
// }
// }
//}
},
} = .{};