Skip to content
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/graphics.zig
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ pub const TextBuffer = struct { // MARK: TextBuffer
return next[0];
}

fn parse(self: *Parser) void {
pub fn parse(self: *Parser) void {
self.curIndex = @intCast(self.unicodeIterator.i);
self.curChar = self.unicodeIterator.nextCodepoint() orelse return;
while (true) {
Expand Down
234 changes: 234 additions & 0 deletions src/log.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
const std = @import("std");
const main = @import("main");
const files = main.files;
const fmt = main.fmt;
const graphics = main.graphics;
const gui = main.gui;
const List = main.List;
const settings = main.settings;

pub const Level = enum {
/// Error: something has gone wrong. This might be recoverable or might
/// be followed by the program exiting.
err,
/// Warning: it is uncertain if something has gone wrong or not, but the
/// circumstances would be worth investigating.
warn,
/// Info: general messages about the state of the program.
info,
/// Debug: messages only useful for debugging.
debug,
/// server messages
server,
/// chat messages
chat,

fn isColorCoded(self: Level) bool {
return self == .chat or self == .server;
}
};

var logFile: ?std.Io.File = undefined;
var logFileTs: ?std.Io.File = undefined;
var supportsANSIColors: bool = undefined;
var openingErrorWindow: bool = false;

// overwrite the log function:
pub const std_options: std.Options = .{ // MARK: std_options
Comment thread
Wunka marked this conversation as resolved.
Outdated
.log_level = .debug,
.logFn = struct {
pub fn logFn(
comptime level: std.log.Level,
comptime _: @EnumLiteral(),
comptime format: []const u8,
args: anytype,
) void {
var runtimeArgs: [args.len]fmt.FormatArg = undefined;
inline for (0..args.len) |i| {
runtimeArgs[i] = .fromAnytype(@TypeOf(args[i]), &args[i]);
}
runtimeLogFn(level, format, &runtimeArgs);
}
}.logFn,
};

noinline fn runtimeLogFn(level: Level, format: []const u8, args: []const fmt.FormatArg) void {
var buf: [65536]u8 = undefined;
var writer: std.Io.Writer = .fixed(&buf);
fmt.format(&writer, format, args) catch {
std.log.err("Truncated long log message.", .{});
};

const color: []const u8 = switch (level) {
.err => "\x1b[31m",
.info => "",
.warn => "\x1b[33m",
.debug => "\x1b[37;44m",
.server => "\x1b[34mserver\x1b[0m: ",
.chat => "\x1b[36mchat\x1b[0m: ",
};
const colorReset = "\x1b[0m\n";
const filePrefix = switch (level) {
.err => "error",
.warn => "warning",
.info => "info",
.debug => "debug",
.server => "server",
.chat => "chat",
};
const fileSuffix = "\n";

logToFile("[{s}]: {s}{s}", .{filePrefix, writer.buffered(), fileSuffix});
if (supportsANSIColors) {
logToStdErr(level, "{s}{s}{s}", .{color, writer.buffered(), colorReset});
} else {
logToStdErr(level, "[{s}]: {s}{s}", .{filePrefix, writer.buffered(), fileSuffix});
}

if (level == .err and !openingErrorWindow and !settings.launchConfig.headlessServer) {
openingErrorWindow = true;
gui.openWindow("error_prompt");
openingErrorWindow = false;
}
}

pub fn init() void {
logFile = null;
files.cwd().makePath("logs") catch |err| {
std.log.err("Couldn't create logs folder: {s}", .{@errorName(err)});
return;
};
logFile = std.Io.Dir.cwd().createFile(main.io, "logs/latest.log", .{}) catch |err| {
std.log.err("Couldn't create logs/latest.log: {s}", .{@errorName(err)});
return;
};

const _timestamp = std.Io.Clock.Timestamp.now(main.io, .real).raw;

const _path_str = std.fmt.allocPrint(main.stackAllocator.allocator, "logs/ts_{}.log", .{_timestamp.nanoseconds}) catch unreachable;
defer main.stackAllocator.free(_path_str);

logFileTs = std.Io.Dir.cwd().createFile(main.io, _path_str, .{}) catch |err| {
std.log.err("Couldn't create {s}: {s}", .{_path_str, @errorName(err)});
return;
};

supportsANSIColors = std.Io.File.stdout().supportsAnsiEscapeCodes(main.io) catch unreachable;
}

pub fn deinit() void {
if (logFile) |_logFile| {
_logFile.close(main.io);
logFile = null;
}

if (logFileTs) |_logFileTs| {
_logFileTs.close(main.io);
logFileTs = null;
}
}

fn logToFile(comptime format: []const u8, args: anytype) void {
var buf: [65536]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buf);
const allocator = fba.allocator();

const string = std.fmt.allocPrint(allocator, format, args) catch format;
(logFile orelse return).writeStreamingAll(main.io, string) catch {};
(logFileTs orelse return).writeStreamingAll(main.io, string) catch {};
}

fn logToStdErr(level: Level, comptime format: []const u8, args: anytype) void {
var buf: [65536]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buf);
const allocator = fba.allocator();

const _string = std.fmt.allocPrint(allocator, format, args) catch format;
const string = if (level.isColorCoded() and supportsANSIColors) convertColorToANSI(_string) else _string;
defer if (level.isColorCoded() and supportsANSIColors) main.stackAllocator.free(string);

const writer = std.debug.lockStderr(&.{});
defer std.debug.unlockStderr();
nosuspend writer.file_writer.interface.writeAll(string) catch {};
}

pub fn convertColorToANSI(text: []const u8) []const u8 {
Comment thread
Wunka marked this conversation as resolved.
Outdated
var list: List(u8) = .empty;

var parser = graphics.TextBuffer.Parser{
.unicodeIterator = std.unicode.Utf8Iterator{.bytes = text, .i = 0},
.currentFontEffect = .{},
.parsedText = .init(main.stackAllocator),
.fontEffects = .init(main.stackAllocator),
.characterIndex = .init(main.stackAllocator),
.showControlCharacters = false,
};
defer parser.fontEffects.deinit();
defer parser.parsedText.deinit();
defer parser.characterIndex.deinit();
parser.parse();

parser.currentFontEffect = .{};
Comment thread
Wunka marked this conversation as resolved.
Outdated
for (0..parser.parsedText.items.len) |i| {
if (parser.fontEffects.items[i].color != parser.currentFontEffect.color) {
list.appendSlice(main.stackAllocator, "\x1b[38;2");
var shift: u5 = 16;
while (true) : (shift -= 8) {
list.print(main.stackAllocator, ";{d}", .{@as(u8, @truncate(parser.fontEffects.items[i].color >> shift))});
if (shift == 0) break;
}
list.append(main.stackAllocator, 'm');
}
if (parser.fontEffects.items[i].bold != parser.currentFontEffect.bold) {
list.appendSlice(main.stackAllocator, "\x1b[");
Comment thread
Wunka marked this conversation as resolved.
Outdated
if (!parser.currentFontEffect.bold) {
list.append(main.stackAllocator, '1');
} else {
list.appendSlice(main.stackAllocator, "22");
}
list.append(main.stackAllocator, 'm');
}
if (parser.fontEffects.items[i].italic != parser.currentFontEffect.italic) {
list.appendSlice(main.stackAllocator, "\x1b[");
if (parser.currentFontEffect.italic) {
list.append(main.stackAllocator, '2');
}
list.appendSlice(main.stackAllocator, "3m");
}
if (parser.fontEffects.items[i].strikethrough != parser.currentFontEffect.strikethrough) {
list.appendSlice(main.stackAllocator, "\x1b[");
if (parser.currentFontEffect.strikethrough) {
list.append(main.stackAllocator, '2');
}
list.appendSlice(main.stackAllocator, "9m");
}
if (parser.fontEffects.items[i].underline != parser.currentFontEffect.underline) {
list.appendSlice(main.stackAllocator, "\x1b[");
if (parser.currentFontEffect.underline) {
list.append(main.stackAllocator, '2');
}
list.appendSlice(main.stackAllocator, "4m");
}
parser.currentFontEffect = parser.fontEffects.items[i];
var testBuff: [3]u8 = undefined;
const len = std.unicode.utf8Encode(@truncate(parser.parsedText.items[i]), &testBuff) catch continue;
Comment thread
Wunka marked this conversation as resolved.
Outdated
list.appendSlice(main.stackAllocator, testBuff[0..len]);
}
return list.toOwnedSlice(main.stackAllocator);
}

pub fn server(comptime format: []const u8, args: anytype) void {
var runtimeArgs: [args.len]fmt.FormatArg = undefined;
inline for (0..args.len) |i| {
runtimeArgs[i] = .fromAnytype(@TypeOf(args[i]), &args[i]);
}
runtimeLogFn(.server, format, &runtimeArgs);
}

pub fn chat(comptime format: []const u8, args: anytype) void {
var runtimeArgs: [args.len]fmt.FormatArg = undefined;
inline for (0..args.len) |i| {
runtimeArgs[i] = .fromAnytype(@TypeOf(args[i]), &args[i]);
}
runtimeLogFn(.chat, format, &runtimeArgs);
}
120 changes: 3 additions & 117 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub const game = @import("game.zig");
pub const graphics = @import("graphics.zig");
pub const itemdrop = @import("itemdrop.zig");
pub const items = @import("items.zig");
pub const log = @import("log.zig");
pub const meta = @import("meta.zig");
pub const migrations = @import("migrations.zig");
pub const models = @import("models.zig");
Expand Down Expand Up @@ -86,121 +87,6 @@ fn cacheStringImpl(comptime len: usize, comptime str: [len]u8) []const u8 {
fn cacheString(comptime str: []const u8) []const u8 {
return cacheStringImpl(str.len, str[0..].*);
}
var logFile: ?std.Io.File = undefined;
var logFileTs: ?std.Io.File = undefined;
var supportsANSIColors: bool = undefined;
var openingErrorWindow: bool = false;
// overwrite the log function:
pub const std_options: std.Options = .{ // MARK: std_options
.log_level = .debug,
.logFn = struct {
pub fn logFn(
comptime level: std.log.Level,
comptime _: @EnumLiteral(),
comptime format: []const u8,
args: anytype,
) void {
var runtimeArgs: [args.len]fmt.FormatArg = undefined;
inline for (0..args.len) |i| {
runtimeArgs[i] = .fromAnytype(@TypeOf(args[i]), &args[i]);
}
runtimeLogFn(level, format, &runtimeArgs);
}
}.logFn,
};

noinline fn runtimeLogFn(level: std.log.Level, format: []const u8, args: []const fmt.FormatArg) void {
var buf: [65536]u8 = undefined;
var writer: std.Io.Writer = .fixed(&buf);
fmt.format(&writer, format, args) catch {
std.log.err("Truncated long log message.", .{});
};

const color: []const u8 = switch (level) {
std.log.Level.err => "\x1b[31m",
std.log.Level.info => "",
std.log.Level.warn => "\x1b[33m",
std.log.Level.debug => "\x1b[37;44m",
};
const colorReset = "\x1b[0m\n";
const filePrefix = switch (level) {
.err => "error",
.warn => "warning",
.info => "info",
.debug => "debug",
};
const fileSuffix = "\n";

logToFile("[{s}]: {s}{s}", .{filePrefix, writer.buffered(), fileSuffix});
if (supportsANSIColors) {
logToStdErr("{s}{s}{s}", .{color, writer.buffered(), colorReset});
} else {
logToStdErr("[{s}]: {s}{s}", .{filePrefix, writer.buffered(), fileSuffix});
}

if (level == .err and !openingErrorWindow and !settings.launchConfig.headlessServer) {
openingErrorWindow = true;
gui.openWindow("error_prompt");
openingErrorWindow = false;
}
}

fn initLogging() void {
logFile = null;
files.cwd().makePath("logs") catch |err| {
std.log.err("Couldn't create logs folder: {s}", .{@errorName(err)});
return;
};
logFile = std.Io.Dir.cwd().createFile(io, "logs/latest.log", .{}) catch |err| {
std.log.err("Couldn't create logs/latest.log: {s}", .{@errorName(err)});
return;
};

const _timestamp = std.Io.Clock.Timestamp.now(io, .real).raw;

const _path_str = std.fmt.allocPrint(stackAllocator.allocator, "logs/ts_{}.log", .{_timestamp.nanoseconds}) catch unreachable;
defer stackAllocator.free(_path_str);

logFileTs = std.Io.Dir.cwd().createFile(io, _path_str, .{}) catch |err| {
std.log.err("Couldn't create {s}: {s}", .{_path_str, @errorName(err)});
return;
};

supportsANSIColors = std.Io.File.stdout().supportsAnsiEscapeCodes(io) catch unreachable;
}

fn deinitLogging() void {
if (logFile) |_logFile| {
_logFile.close(io);
logFile = null;
}

if (logFileTs) |_logFileTs| {
_logFileTs.close(io);
logFileTs = null;
}
}

fn logToFile(comptime format: []const u8, args: anytype) void {
var buf: [65536]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buf);
const allocator = fba.allocator();

const string = std.fmt.allocPrint(allocator, format, args) catch format;
(logFile orelse return).writeStreamingAll(io, string) catch {};
(logFileTs orelse return).writeStreamingAll(io, string) catch {};
}

fn logToStdErr(comptime format: []const u8, args: anytype) void {
var buf: [65536]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buf);
const allocator = fba.allocator();

const string = std.fmt.allocPrint(allocator, format, args) catch format;
const writer = std.debug.lockStderr(&.{});
defer std.debug.unlockStderr();
nosuspend writer.file_writer.interface.writeAll(string) catch {};
}

// MARK: Callbacks
fn escape(mods: Window.Key.Modifiers) void {
Expand Down Expand Up @@ -434,8 +320,8 @@ pub fn main(args: std.process.Init.Minimal) void { // MARK: main()
threadedIo = .init(globalAllocator.allocator, .{});
defer threadedIo.deinit();

initLogging();
defer deinitLogging();
log.init();
defer log.deinit();

std.log.info("Starting game with version {s}", .{settings.version.version});

Expand Down
2 changes: 1 addition & 1 deletion src/server/server.zig
Original file line number Diff line number Diff line change
Expand Up @@ -824,7 +824,7 @@ pub fn messageFrom(msg: []const u8, source: *User) void { // MARK: message
fn sendRawMessage(msg: []const u8) void {
chatMutex.lock();
defer chatMutex.unlock();
std.log.info("Chat: {s}", .{msg}); // TODO use color \033[0;32m
main.log.chat("{s}", .{msg});
const userList = getUserListAndIncreaseRefCount(main.stackAllocator);
defer freeUserListAndDecreaseRefCount(main.stackAllocator, userList);
for (userList) |user| {
Expand Down
Loading
Loading