Read description VVV

World edit mod, only selects for now.
Text Formatting
Tweak default font to look good in bold.
Fix Server locking up when too many player packets are sent.
Added chat mod, which *almost* works.
Update world noise.
master
Auri 2021-08-15 17:21:12 -07:00
parent 8bb8752b73
commit a077065aec
64 changed files with 1201 additions and 650 deletions

View File

@ -15,4 +15,16 @@ string.split = function(input, sep, op)
for str in string.gmatch(input, '([^'..sep..']+)') do
table.insert(t, op and op(str) or str) end
return t
end
-- string.escape
-- Escapes a string, replacing all graves with grave literals.
string.escape = function(str)
return str:gsub('%`', '``')
end
-- string.starts_with
-- Checks if a string starts with the specified substring.
string.starts_with = function(str, substr)
return str:sub(1, substr:len()) == substr
end

View File

@ -50,7 +50,7 @@ vector.__unm = vector.negative
-- vector.subtract
-- Subtract v2 from v1
function vector.subtract(v1, v2)
return vector.add(v1, vector.negative(v2))
return vector.add(v1, -v2)
end
vector.__sub = vector.subtract
@ -59,11 +59,8 @@ vector.__sub = vector.subtract
-- Multiply v1 by a vector or number
function vector.multiply(v1, m)
assert(vector.is_vector(v1))
if vector.is_vector(m) then
return create_vector(rawget(v1, 1) * rawget(m, 1), rawget(v1, 2) * rawget(m, 2), rawget(v1, 3) * rawget(m, 3))
elseif type(m) == "number" then
return create_vector(rawget(v1, 1) * m, rawget(v1, 2) * m, rawget(v1, 3) * m)
end
if vector.is_vector(m) then return create_vector(rawget(v1, 1) * rawget(m, 1), rawget(v1, 2) * rawget(m, 2), rawget(v1, 3) * rawget(m, 3))
elseif type(m) == "number" then return create_vector(rawget(v1, 1) * m, rawget(v1, 2) * m, rawget(v1, 3) * m) end
end
vector.__mul = vector.multiply
@ -176,10 +173,10 @@ end
function vector:__tostring()
return table.concat({
"{ ",
"{",
tostring(rawget(self, 1)), ", ",
tostring(rawget(self, 2)), ", ",
tostring(rawget(self, 3)), " }"
tostring(rawget(self, 3)), "}"
})
end
@ -202,7 +199,7 @@ vector.new = function(x, y, z)
-- Invalid type passed to function, return nil
elseif type(x) ~= "number" and type(x) ~= "table" then return nil
-- Passed a table as x with at least x and y parameters, z will be set to table value or 0
elseif type(x) == "table" and (x.__is_vector or (type(x[1]) == "number" and type(x[2]) == "number")) then return create_vector(x[1], x[2], x[3] or 0)
elseif type(x) == "table" and (vector.is_vector(x) or (type(x[1]) == "number" and type(x[2]) == "number")) then return create_vector(x[1], x[2], x[3] or 0)
-- Only one number was passed, give a vector with all three values set to it
elseif y == nil then return create_vector(x, x, x)
-- Invalid type passed to function, return nil

View File

@ -15,11 +15,11 @@ uniform sampler2D tex;
void main() {
if (useTex > 0.5) {
vec4 spec = texture(tex, colorData.xy) * vec4(colorBlend, colorData.w);
if (spec.a < 0.1) discard;
if (spec.a < 0.01) discard;
gSpecular = spec;
}
else {
if (colorData.a < 0.1) discard;
if (colorData.a < 0.01) discard;
gSpecular = colorData * vec4(colorBlend, 1);
}
gPosition = vec4(fragPos, 1);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 953 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

View File

@ -1,79 +1,114 @@
//
// Created by aurailus on 08/04/19.
//
#include "WireframeEntity.h"
#include "world/dim/ent/DrawableEntity.h"
void WireframeEntity::updateMesh(const std::vector<SelectionBox>& boxes, float width) {
this->width = width;
buildMesh(boxes);
this->model->fromMesh(WireframeEntity::createMesh(boxes, width));
}
void WireframeEntity::buildMesh(const std::vector<SelectionBox>& boxes) {
indOffset = 0;
uptr<EntityMesh> WireframeEntity::createMesh(const vec<SelectionBox>& boxes, f32 wireWidth, vec3 stroke, vec4 fill) {
vec<u32> indices {};
indices.reserve(boxes.size() * 36 * (12 + fill.a ? 1 : 0));
vec<EntityVertex> vertices {};
vertices.reserve(boxes.size() * 8 * 12 + (fill.a ? 24 : 0));
vertices.clear();
indices.clear();
for (const let& box : boxes) WireframeEntity::createSelectionBoxVertices(
box, wireWidth, vec4(stroke, 1), fill, vertices, indices);
for (auto& box : boxes) {
glm::vec3 a = box.a;
glm::vec3 b = box.b - box.a;
createBox(a, b, 0, 0, 0, 0, b.y, 0);
createBox(a, b, b.x, 0, 0, 0, b.y, 0);
createBox(a, b, b.x, 0, b.z, 0, b.y, 0);
createBox(a, b, 0, 0, b.z, 0, b.y, 0);
createBox(a, b, 0, 0, 0, b.x, 0, 0);
createBox(a, b, 0, b.y, 0, b.x, 0, 0);
createBox(a, b, 0, b.y, b.z, b.x, 0, 0);
createBox(a, b, 0, 0, b.z, b.x, 0, 0);
createBox(a, b, 0, 0, 0, 0, 0, b.z);
createBox(a, b, 0, b.y, 0, 0, 0, b.z);
createBox(a, b, b.x, b.y, 0, 0, 0, b.z);
createBox(a, b, b.x, 0, 0, 0, 0, b.z);
}
std::unique_ptr<EntityMesh> mesh = std::make_unique<EntityMesh>();
uptr<EntityMesh> mesh = make_unique<EntityMesh>();
mesh->create(vertices, indices);
this->model->fromMesh(std::move(mesh));
return mesh;
}
void
WireframeEntity::createBox(glm::vec3 a, glm::vec3 b, float x, float y, float z, float xSize, float ySize, float zSize) {
float hw = (width / 2.0f);
float w = width;
glm::vec3 c = color;
uptr<EntityMesh> WireframeEntity::createMesh(const SelectionBox& box, f32 wireWidth, vec3 stroke, vec4 fill) {
return WireframeEntity::createMesh(vec<SelectionBox> { box }, wireWidth, stroke, fill);
}
void WireframeEntity::createSelectionBoxVertices(const SelectionBox& box, f32 wireWidth,
vec4 stroke, vec4 fill, vec<EntityVertex>& vertices, vec<u32>& indices) {
std::vector<EntityVertex> myVerts{
/*0*/
{{ x - hw + a.x, y - hw + a.y, z - hw + a.z }, { c.x, c.y, c.z, 1 }, { 1, 1, 1 }, false, { 0, 1, 0 }, {}, {}},
/*1*/{{ x - hw + a.x + xSize + w, y - hw + a.y, z - hw + a.z }, { c.x, c.y, c.z, 1 }, { 1, 1, 1 }, false,
{ 0, 1, 0 }, {}, {}},
/*2*/
{{ x - hw + a.x + xSize + w, y - hw + a.y, z - hw + a.z + zSize + w }, { c.x, c.y, c.z, 1 }, { 1, 1, 1 }, false,
{ 0, 1, 0 }, {}, {}},
/*3*/{{ x - hw + a.x, y - hw + a.y, z - hw + a.z + zSize + w }, { c.x, c.y, c.z, 1 }, { 1, 1, 1 }, false,
{ 0, 1, 0 }, {}, {}},
let& a = box.a;
let& b = box.b;
createStrokeVertices({ a.x, a.y, a.z }, { b.x, a.y, a.z }, wireWidth, stroke, vertices, indices);
createStrokeVertices({ a.x, a.y, b.z }, { b.x, a.y, b.z }, wireWidth, stroke, vertices, indices);
createStrokeVertices({ a.x, a.y, a.z }, { a.x, a.y, b.z }, wireWidth, stroke, vertices, indices);
createStrokeVertices({ b.x, a.y, a.z }, { b.x, a.y, b.z }, wireWidth, stroke, vertices, indices);
createStrokeVertices({ a.x, b.y, a.z }, { b.x, b.y, a.z }, wireWidth, stroke, vertices, indices);
createStrokeVertices({ a.x, b.y, b.z }, { b.x, b.y, b.z }, wireWidth, stroke, vertices, indices);
createStrokeVertices({ a.x, b.y, a.z }, { a.x, b.y, b.z }, wireWidth, stroke, vertices, indices);
createStrokeVertices({ b.x, b.y, a.z }, { b.x, b.y, b.z }, wireWidth, stroke, vertices, indices);
createStrokeVertices({ a.x, a.y, a.z }, { a.x, b.y, a.z }, wireWidth, stroke, vertices, indices);
createStrokeVertices({ b.x, a.y, a.z }, { b.x, b.y, a.z }, wireWidth, stroke, vertices, indices);
createStrokeVertices({ b.x, a.y, b.z }, { b.x, b.y, b.z }, wireWidth, stroke, vertices, indices);
createStrokeVertices({ a.x, a.y, b.z }, { a.x, b.y, b.z }, wireWidth, stroke, vertices, indices);
if (fill.a) {
vec3 nml = { 0, 1, 0 };
f32 off = wireWidth / 2;
usize indOffset = vertices.size();
/*4*/{{ x - hw + a.x, y - hw + a.y + ySize + w, z - hw + a.z }, { c.x, c.y, c.z, 1 }, { 1, 1, 1 }, false,
{ 0, 1, 0 }, {}, {}},
/*5*/
{{ x - hw + a.x + xSize + w, y - hw + a.y + ySize + w, z - hw + a.z }, { c.x, c.y, c.z, 1 }, { 1, 1, 1 }, false,
{ 0, 1, 0 }, {}, {}},
/*6*/{{ x - hw + a.x + xSize + w, y - hw + a.y + ySize + w, z - hw + a.z + zSize + w }, { c.x, c.y, c.z, 1 },
{ 1, 1, 1 }, false, { 0, 1, 0 }, {}, {}},
/*7*/
{{ x - hw + a.x, y - hw + a.y + ySize + w, z - hw + a.z + zSize + w }, { c.x, c.y, c.z, 1 }, { 1, 1, 1 }, false,
{ 0, 1, 0 }, {}, {}},
};
vertices.push_back({{ a.x, a.y, a.z - off }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ a.x, b.y, a.z - off }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ b.x, b.y, a.z - off }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ b.x, a.y, a.z - off }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ a.x, a.y, b.z + off }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ a.x, b.y, b.z + off }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ b.x, b.y, b.z + off }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ b.x, a.y, b.z + off }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ a.x - off, a.y, a.z }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ a.x - off, b.y, a.z }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ a.x - off, b.y, b.z }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ a.x - off, a.y, b.z }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ b.x + off, a.y, a.z }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ b.x + off, b.y, a.z }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ b.x + off, b.y, b.z }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ b.x + off, a.y, b.z }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ a.x, a.x - off, a.z }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ b.x, a.x - off, a.z }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ b.x, a.x - off, b.z }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ a.x, a.x - off, b.z }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ a.x, b.y + off, a.z }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ b.x, b.y + off, a.z }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ b.x, b.y + off, b.z }, fill, vec3(1), false, nml, {}, {}});
vertices.push_back({{ a.x, b.y + off, b.z }, fill, vec3(1), false, nml, {}, {}});
const static array<u32, 36> boxIndices {
0, 1, 2, 2, 3, 0,
4, 7, 6, 6, 5, 4,
8, 11, 10, 10, 9, 8,
12, 13, 14, 14, 15, 12,
16, 17, 18, 18, 19, 16,
20, 23, 22, 22, 21, 20
};
for (u32 i : boxIndices) indices.push_back(i + indOffset);
}
}
void WireframeEntity::createStrokeVertices(vec3 a, vec3 b, f32 wireWidth,
vec4 color, vec<EntityVertex>& vertices, vec<u32>& indices) {
std::vector<unsigned int> myInds{
f32 off = wireWidth / 2.f;
vec3 nml = { 0, 1, 0 };
usize indOffset = vertices.size();
vertices.push_back({{ a.x - off, a.y - off, a.z - off }, color, vec3(1), false, nml, {}, {}});
vertices.push_back({{ b.x + off, a.y - off, a.z - off }, color, vec3(1), false, nml, {}, {}});
vertices.push_back({{ b.x + off, a.y - off, b.z + off }, color, vec3(1), false, nml, {}, {}});
vertices.push_back({{ a.x - off, a.y - off, b.z + off }, color, vec3(1), false, nml, {}, {}});
vertices.push_back({{ a.x - off, b.y + off, a.z - off }, color, vec3(1), false, nml, {}, {}});
vertices.push_back({{ b.x + off, b.y + off, a.z - off }, color, vec3(1), false, nml, {}, {}});
vertices.push_back({{ b.x + off, b.y + off, b.z + off }, color, vec3(1), false, nml, {}, {}});
vertices.push_back({{ a.x - off, b.y + off, b.z + off }, color, vec3(1), false, nml, {}, {}});
const static array<u32, 36> boxIndices {
0, 1, 2, 2, 3, 0,
4, 7, 6, 6, 5, 4,
0, 4, 5, 5, 1, 0,
@ -82,8 +117,5 @@ WireframeEntity::createBox(glm::vec3 a, glm::vec3 b, float x, float y, float z,
1, 5, 6, 6, 2, 1,
};
vertices.insert(vertices.end(), myVerts.begin(), myVerts.end());
for (auto i : myInds) indices.push_back(i + indOffset);
indOffset += 8;
for (u32 i : boxIndices) indices.push_back(i + indOffset);
}

View File

@ -1,7 +1,3 @@
//
// Created by aurailus on 08/04/19.
//
#pragma once
#include "world/dim/ent/DrawableEntity.h"
@ -11,22 +7,24 @@
class WireframeEntity : public DrawableEntity {
public:
WireframeEntity(SubgamePtr game, DimensionPtr dim, glm::vec3 color) :
DrawableEntity(game, dim, std::make_shared<Model>()), Entity(game, dim),
color(color) {};
WireframeEntity(SubgamePtr game, DimensionPtr dim, vec3 stroke, vec4 fill = vec4(0)) :
DrawableEntity(game, dim, std::make_shared<Model>()), Entity(game, dim), stroke(stroke), fill(fill) {};
void updateMesh(const std::vector<SelectionBox>& boxes, float width);
void updateMesh(const vec<SelectionBox>& boxes, f32 width);
static uptr<EntityMesh> createMesh(const vec<SelectionBox>& boxes,
f32 wireWidth, vec3 stroke = vec3(1), vec4 fill = vec4(0));
static uptr<EntityMesh> createMesh(const SelectionBox& box,
f32 wireWidth, vec3 stroke = vec3(1), vec4 fill = vec4(0));
private:
std::vector<EntityVertex> vertices{};
std::vector<unsigned int> indices{};
static void createSelectionBoxVertices(const SelectionBox& box, f32 wireWidth,
vec4 stroke, vec4 fill, vec<EntityVertex>& vertices, vec<u32>& indices);
void buildMesh(const std::vector<SelectionBox>& boxes);
static void createStrokeVertices(vec3 a, vec3 b, f32 wireWidth,
vec4 color, vec<EntityVertex>& vertices, vec<u32>& indices);
void createBox(glm::vec3 a, glm::vec3 b, float x, float y, float z, float xSize, float ySize, float zSize);
glm::vec3 color{};
float width = 0.5;
int indOffset = 0;
vec3 stroke {};
vec4 fill {};
};

View File

@ -93,6 +93,8 @@ void Renderer::beginChunkDeferredCalls() {
}
void Renderer::beginEntityDeferredCalls() {
glEnable(GL_BLEND);
currentModelUniform = entity.uniforms.model;
setShader(entity);
@ -101,6 +103,8 @@ void Renderer::beginEntityDeferredCalls() {
}
void Renderer::endDeferredCalls() {
glDisable(GL_BLEND);
activeTexture = nullptr;
glBindFramebuffer(GL_FRAMEBUFFER, ssao.fbo);
@ -154,6 +158,7 @@ void Renderer::endDeferredCalls() {
glBindTexture(GL_TEXTURE_2D, blur.colorBuffer);
glEnable(GL_BLEND);
renderQuad();
}

View File

@ -21,11 +21,11 @@ DebugGui::DebugGui(u16vec2 buffer, SubgamePtr game, LocalWorld& world, vec<strin
Font f(game.l()->textures, fontRef);
auto crosshairText = make_shared<GuiText>("crosshairText");
crosshairText->create({ 2, 2 }, {}, { 0.2, 0.2, 0.2, 0.5 }, { 1, 1, 1, 1 }, f);
crosshairText->create({ 2, 2 }, {}, { 1, 1, 1, 1 }, { 0.2, 0.2, 0.2, 0.5 }, f);
add(crosshairText);
auto dataText = make_shared<GuiText>("dataText");
dataText->create({ 2, 2 }, {}, { 0.2, 0.2, 0.2, 0.5 }, { 1, 1, 1, 1 }, f);
dataText->create({ 2, 2 }, {}, { 1, 1, 1, 1 }, { 0.2, 0.2, 0.2, 0.5 }, f);
add(dataText);
auto interpGraph = make_shared<GuiLabelledGraph>("interpGraph");
@ -219,7 +219,8 @@ void DebugGui::update(sptr<LocalPlayer> player, f64 delta, u32 interpolatedChunk
if (target.type == Target::Type::BLOCK) {
const auto& def = game->getDefs().blockFromId(world.getActiveDimension()->getBlock(target.data.block.pos));
get<GuiText>("crosshairText")->setText(def.name + " (" + def.identifier + ") [" + std::to_string(def.index) + "]");
get<GuiText>("crosshairText")->setText(
"`b" + def.name + " (`r` `c7" + def.identifier + "`cr - " + std::to_string(def.index) + "` `b)");
}
else {
get<GuiText>("crosshairText")->setText("");

View File

@ -43,7 +43,7 @@ void GuiInventoryItem::create(glm::vec2 scale, unsigned short count, ItemDef& de
if (count > 1) {
auto text = std::make_shared<GuiText>("count");
text->create(scale, {}, {}, { 1, 1, 1, 1 }, f);
text->create(scale, {}, { 1, 1, 1, 1 }, {}, f);
text->setText(std::to_string(count));
add(text);
text->setPos({ (19 - text->getWidth()) * scale.x, 9 * scale.y });

View File

@ -1,9 +1,3 @@
//
// Created by aurailus on 25/12/18.
//
#include <utility>
#include "GuiText.h"
#include "util/Util.h"
@ -12,178 +6,260 @@
#include "game/atlas/asset/AtlasRef.h"
#include "world/dim/ent/AnimationSegment.h"
GuiText::GuiText(const std::string& key) : GuiComponent(key) {}
void GuiText::create(glm::vec2 scale, glm::vec4 padding, glm::vec4 bgcolor, glm::vec4 color, Font font) {
// Text Constructor
// Creates a GuiText object.
this->scale = scale;
void GuiText::create(vec2 scale, vec4 padding, vec4 textColor, vec4 backgroundColor, Font font) {
this->padding = padding;
this->font = std::move(font);
this->bgcolor = bgcolor;
this->color = color;
this->textColor = textColor;
this->backgroundColor = backgroundColor;
setScale(scale);
setText("");
setScale(scale);
}
std::shared_ptr<GuiText> GuiText::fromSerialized(const LuaGuiElement& elem, TextureAtlas& textures, glm::ivec2 bounds) {
glm::vec2 pos = SerialGui::get<glm::vec2>(elem, "position", bounds);
glm::vec2 offset = SerialGui::get<glm::vec2>(elem, "position_anchor");
glm::vec2 size = SerialGui::get<glm::vec2>(elem, "size", bounds);
glm::vec4 padding = SerialGui::get<glm::vec4>(elem, "padding", bounds);
glm::vec2 scale = SerialGui::get<glm::vec2>(elem, "scale");
if (scale == glm::vec2{ 0, 0 }) scale = { 1, 1 };
sptr<GuiText> GuiText::fromSerialized(const LuaGuiElement& elem, TextureAtlas& textures, ivec2 bounds) {
vec2 pos = SerialGui::get<vec2>(elem, "position", bounds);
vec2 offset = SerialGui::get<vec2>(elem, "position_anchor");
vec2 size = SerialGui::get<vec2>(elem, "size", bounds);
vec4 padding = SerialGui::get<vec4>(elem, "padding", bounds);
vec2 scale = SerialGui::get<vec2>(elem, "scale") * vec2(0.33);
if (scale.x == 0 && scale.y == 0) scale = vec2 { 1, 1 };
pos -= offset * size;
glm::vec4 background_color = Util::hexToColorVec(elem.get_or<std::string>("background", "#0000"));
glm::vec4 color = Util::hexToColorVec(elem.get_or<std::string>("color", "#fff"));
std::string content = elem.get_or<std::string>("content", "");
vec4 backgroundColor = Util::hexToColorVec(elem.get_or<string>("background", "#0000"));
vec4 textColor = Util::hexToColorVec(elem.get_or<string>("color", "#fff"));
string content = elem.get_or<string>("content", "");
auto text = std::make_shared<GuiText>(elem.key);
text->create(scale * SerialGui::SCALE_MODIFIER, padding, background_color, color, { textures, textures["font"] });
text->create(scale * SerialGui::SCALE_MODIFIER, padding,
textColor, backgroundColor, { textures, textures["font"] });
text->setText(content);
text->setPos(pos);
return text;
}
void GuiText::setText(std::string text) {
this->text = std::move(text);
unsigned int indOffset = 0;
void GuiText::setText(string newText) {
text = newText;
u32 ind = 0;
maxLineWidth = 0;
width = 0;
std::vector<EntityVertex> textVertices;
textVertices.reserve(text.length() * 8 + 200);
std::vector<unsigned int> textIndices;
textIndices.reserve(text.length() * 12 + 240);
vec<EntityVertex> vertices;
vertices.reserve(text.length() * 8 + 200);
vec<u32> indices;
indices.reserve(text.length() * 12 + 240);
//Draw background & Measure Line Width
int lineWidth = 0;
int xOffset = 0, yOffset = 0;
int h = Font::charHeight;
for (unsigned int i = 0; i < this->text.length() + 1; i++) {
char c = this->text[i];
//TODO: Proper font handling.
if (c == '\t') c = ' ';
if (c == '\n' || i == this->text.length()) {
if (lineWidth > 0) {
lineWidth += 2;
if (lineWidth > maxLineWidth) maxLineWidth = lineWidth;
if (bgcolor.w != 0) {
textVertices.emplace_back(glm::vec3{ -1, yOffset - 1, 0 }, bgcolor, glm::vec3(1), 0.f, glm::vec3{},
glm::ivec4{}, glm::vec4{});
textVertices.emplace_back(glm::vec3{ -1, yOffset + h + 1, 0 }, bgcolor, glm::vec3(1), 0.f,
glm::vec3{}, glm::ivec4{}, glm::vec4{});
textVertices.emplace_back(glm::vec3{ lineWidth + 1, yOffset + h + 1, 0 }, bgcolor, glm::vec3(1),
0.f, glm::vec3{}, glm::ivec4{}, glm::vec4{});
textVertices.emplace_back(glm::vec3{ lineWidth + 1, yOffset - 1, 0 }, bgcolor, glm::vec3(1), 0.f,
glm::vec3{}, glm::ivec4{}, glm::vec4{});
textIndices.emplace_back(indOffset);
textIndices.emplace_back(indOffset + 1);
textIndices.emplace_back(indOffset + 2);
textIndices.emplace_back(indOffset + 2);
textIndices.emplace_back(indOffset + 3);
textIndices.emplace_back(indOffset);
indOffset += 4;
}
yOffset += h + 2;
}
else {
yOffset += h / 2; //Pad out the height if using just newlines.
}
lineWidth = 0;
}
else lineWidth += font.getCharWidth(c) + 1;
vec<string> lines;
{
std::stringstream textStream(text);
string line;
while (std::getline(textStream, line, '\n')) lines.emplace_back(line);
}
//Draw Characters
vec3 offset = {};
u32 h = Font::charHeight;
bool emptyLine = true;
xOffset = 0;
yOffset = 0;
bool bold = false;
bool italic = false;
i32 underline = -1;
i32 strikethrough = -1;
u32 strikethroughVertStart = 0;
vec4 color = textColor;
for (unsigned int i = 0; i < this->text.length() + 1; i++) {
char c = this->text[i];
for (usize i = 0; i < lines.size(); i++) {
let& line = lines[i];
bool empty = line.find_first_not_of(" \t\n") == -1;
//TODO: Proper font handling.
if (c == '\t') c = ' ';
unsigned int h = Font::charHeight;
if (c == '\n' || i == this->text.length()) {
yOffset += (emptyLine) ? h / 2 : h + 2;
xOffset = 0;
emptyLine = true;
if (empty) {
offset.x = 0;
offset.y += h / 2;
continue;
}
else {
emptyLine = false;
u32 bgVertStart = 0;
if (backgroundColor.w != 0) {
bgVertStart = vertices.size();
for (u32 i = 0; i < 4; i++) vertices.push_back({});
for (u32 i : INDICES) indices.push_back(i + ind);
ind += 4;
}
auto charWidth = font.getCharWidth(c) + 1;
auto charUVs = font.getCharUVs(c);
for (unsigned int j = 0; j <= 1; j++) {
glm::vec3 c = { this->color.x, this->color.y, this->color.z };
for (usize j = 0; j < line.length() + 1; j++) {
char c = j < line.length() ? line[j] : ' ';
if (c == '\t') c = ' ';
if (j == 0) {
c *= glm::vec3{ 0.4, 0.4, 0.45 };
xOffset += 1;
yOffset += 1;
}
else {
xOffset -= 1;
yOffset -= 1;
if (c == '`') {
bool flushDecorators = j == line.length();
char d = line[++j];
if (d == '`') goto escape_formatting;
else if (d == ' ') offset.x++;
else if (d == 'b') bold = true;
else if (d == 'i') italic = true;
else if (d == 'u') underline = offset.x;
else if (d == 's') {
strikethrough = offset.x;
strikethroughVertStart = vertices.size();
for (u32 i = 0; i < 4; i++) vertices.push_back({});
for (u32 i : INDICES) indices.push_back(i + ind);
ind += 4;
}
else if (d == 'c') flushDecorators = true;
else if (d == 'r') {
bold = false;
italic = false;
flushDecorators = true;
}
if (flushDecorators) {
if (underline != -1) {
GuiText::drawRect({ underline, h - 1, offset.x, h }, color, vertices, indices, ind);
GuiText::drawRect({ underline + 1, h, offset.x + 1, h + 1 },
color * BG_MULTIPLE, vertices, indices, ind);
underline = offset.x;
}
if (strikethrough != -1) {
GuiText::drawRect({ strikethrough, h / 2, offset.x, h / 2 + 1 },
color, vertices, indices, ind);
GuiText::drawRect({ strikethrough + 1, h / 2 + 1, offset.x + 1, h / 2 + 2 },
color * BG_MULTIPLE, vertices, indices, ind, strikethroughVertStart);
strikethrough = offset.x;
}
if (d == 'r') {
color = textColor;
underline = -1;
strikethrough = -1;
}
}
if (d == 'c') {
char code = line[++j];
if (code == 'r') color = textColor;
else {
u32 v;
std::stringstream ss;
ss << std::hex << code;
ss >> v;
color = COLORS[v];
}
}
continue;
}
textVertices.emplace_back(glm::vec3{ xOffset, yOffset, 0 }, glm::vec4{ charUVs.x, charUVs.y, 0, color.w },
c, 1.f, glm::vec3{}, glm::ivec4{}, glm::vec4{});
textVertices.emplace_back(glm::vec3{ xOffset, yOffset + h, 0 },
glm::vec4{ charUVs.x, charUVs.w, 0, color.w }, c, 1.f, glm::vec3{}, glm::ivec4{}, glm::vec4{});
textVertices.emplace_back(glm::vec3{ xOffset + charWidth, yOffset + h, 0 },
glm::vec4{ charUVs.z, charUVs.w, 0, color.w }, c, 1.f, glm::vec3{}, glm::ivec4{}, glm::vec4{});
textVertices.emplace_back(glm::vec3{ xOffset + charWidth, yOffset, 0 },
glm::vec4{ charUVs.z, charUVs.y, 0, color.w }, c, 1.f, glm::vec3{}, glm::ivec4{}, glm::vec4{});
textIndices.emplace_back(indOffset);
textIndices.emplace_back(indOffset + 1);
textIndices.emplace_back(indOffset + 2);
textIndices.emplace_back(indOffset + 2);
textIndices.emplace_back(indOffset + 3);
textIndices.emplace_back(indOffset);
indOffset += 4;
escape_formatting:
if (j == line.length()) continue;
u32 w = font.getCharWidth(c) + 1;
vec4 UV = font.getCharUVs(c);
for (u32 k = 0; k < (bold ? 4 : 2); k++) {
vec4 c = color;
if (k == 0 || (k == 1 && bold)) c *= BG_MULTIPLE;
if (k == 0) {
offset.x += 1;
offset.y += 1;
}
else if ((k == 1 || k == 3) && bold) {
offset.x += 1;
}
else if ((k == 1 && !bold) || (k == 2 && bold)) {
offset.x -= bold ? 2 : 1;
offset.y -= 1;
}
vertices.emplace_back(offset + vec3(italic ? 2 : 0, 0, 0), vec4 { UV.x, UV.y, 0, c.w },
vec3(c), 1.f, vec3 {}, ivec4 {}, vec4 {});
vertices.emplace_back(offset + vec3(0, h, 0), vec4 { UV.x, UV.w, 0, c.w },
vec3(c), 1.f, vec3 {}, ivec4 {}, vec4 {});
vertices.emplace_back(offset + vec3(w, h, 0), vec4 { UV.z, UV.w, 0, c.w },
vec3(c), 1.f, vec3 {}, ivec4 {}, vec4 {});
vertices.emplace_back(offset + vec3(w + (italic ? 2 : 0), 0, 0), vec4 { UV.z, UV.y, 0, c.w },
vec3(c), 1.f, vec3 {}, ivec4 {}, vec4 {});
for (u32 i : INDICES) indices.push_back(i + ind);
ind += 4;
}
offset.x += w;
}
xOffset += charWidth;
if (backgroundColor.w != 0) GuiText::drawRect({ -1, offset.y - 1, offset.x + 2, offset.y + h + 1 },
backgroundColor, vertices, indices, ind, bgVertStart);
if (offset.x > width) width = offset.x;
offset.x = 0;
offset.y += h + 2;
}
auto m = std::make_unique<EntityMesh>();
m->create(textVertices, textIndices);
let m = make_unique<EntityMesh>();
m->create(vertices, indices);
auto model = std::make_shared<Model>();
let model = make_shared<Model>();
model->fromMesh(std::move(m));
entity.setModel(model);
}
std::string GuiText::getText() {
string GuiText::getText() {
return text;
}
unsigned int GuiText::getWidth() {
return maxLineWidth;
u32 GuiText::getWidth() {
return width;
}
void GuiText::drawRect(const vec4 pos, const vec4 color,
vec<EntityVertex>& vertices, vec<u32>& indices, u32& ind, const u32 insert) {
vec<EntityVertex> myVerts = {
{ vec3 { pos.x, pos.y, 0 }, color, vec3(1), 0.f, vec3 {}, ivec4 {}, vec4 {} },
{ vec3 { pos.x, pos.w, 0 }, color, vec3(1), 0.f, vec3 {}, ivec4 {}, vec4 {} },
{ vec3 { pos.z, pos.w, 0 }, color, vec3(1), 0.f, vec3 {}, ivec4 {}, vec4 {} },
{ vec3 { pos.z, pos.y, 0 }, color, vec3(1), 0.f, vec3 {}, ivec4 {}, vec4 {} }
};
if (insert != -1) {
vertices[insert] = myVerts[0];
vertices[insert + 1] = myVerts[1];
vertices[insert + 2] = myVerts[2];
vertices[insert + 3] = myVerts[3];
}
else {
for (EntityVertex& vert : myVerts) vertices.emplace_back(vert);
for (u32 i : INDICES) indices.push_back(i + ind);
ind += 4;
}
}
const array<vec4, 16> GuiText::COLORS = {
Util::hexToColorVec("#000000"),
Util::hexToColorVec("#0000AA"),
Util::hexToColorVec("#00AA00"),
Util::hexToColorVec("#00AAAA"),
Util::hexToColorVec("#AA0000"),
Util::hexToColorVec("#b05cff"),
Util::hexToColorVec("#FFAA00"),
Util::hexToColorVec("#cccccc"),
Util::hexToColorVec("#555555"),
Util::hexToColorVec("#33a2f5"),
Util::hexToColorVec("#9fff80"),
Util::hexToColorVec("#7df4ff"),
Util::hexToColorVec("#ff739f"),
Util::hexToColorVec("#fd7dff"),
Util::hexToColorVec("#fffb82"),
Util::hexToColorVec("#ffffff")
};
const array<u32, 6> GuiText::INDICES = { 0, 1, 2, 2, 3, 0 };
const vec4 GuiText::BG_MULTIPLE = { 0.3, 0.3, 0.35, 0.75 };

View File

@ -1,7 +1,3 @@
//
// Created by aurailus on 25/12/18.
//
#pragma once
#include "client/gui/GuiComponent.h"
@ -9,32 +5,58 @@
#include "client/graph/Font.h"
class TextureAtlas;
class LuaGuiElement;
/**
* Renders text, with formatting escape codes.
*
* `` - Raw grave character
* ` - Single pixel space
* `c[0-f] - Preset colors
* `cr - Reset to the global text color
* `#[hex code] - Color with an arbitrary hex code
* `b - Bold
* `i - Italic
* `u - Underline
* `s - Strikethrough
* `r - Reset to default formatting
*/
class GuiText : public GuiComponent {
public:
public:
GuiText() = default;
explicit GuiText(const std::string& key);
explicit GuiText(const string& key) : GuiComponent(key) {};
/** Creates a text component from a serialized lua element. */
static sptr<GuiText> fromSerialized(const LuaGuiElement& elem, TextureAtlas& textures, ivec2 bounds);
/** Creates a new text component. */
void create(vec2 scale, vec4 padding, vec4 textColor, vec4 backgroundColor, Font font);
/** Returns the width in display pixels of the text. */
u32 getWidth();
static std::shared_ptr<GuiText>
fromSerialized(const LuaGuiElement& elem, TextureAtlas& textures, glm::ivec2 bounds);
/** Sets the text to the string provided. */
void setText(string text);
void create(glm::vec2 scale, glm::vec4 padding, glm::vec4 bgcolor, glm::vec4 color, Font font);
/** Returns the current text string. */
string getText();
unsigned int getWidth();
void setText(std::string text);
std::string getText();
private:
private:
Font font;
glm::vec4 bgcolor{};
glm::vec4 color{};
std::string text;
unsigned int maxLineWidth = 0;
void drawRect(const vec4 pos, const vec4 color,
vec<EntityVertex>& vertices, vec<u32>& indices, u32& ind, const u32 insert = -1);
string text;
vec4 textColor {};
vec4 backgroundColor {};
u32 width = 0;
static const array<vec4, 16> COLORS;
static const array<u32, 6> INDICES;
static const vec4 BG_MULTIPLE;
};

View File

@ -18,7 +18,7 @@ void GuiCellGraph::create(f32 scale, vec4 padding, u16 count, const string& titl
add(background);
let label = make_shared<GuiText>();
label->create({ 2, 2 }, {}, {}, { 1, 1, 1, 1 }, this->font);
label->create({ 2, 2 }, {}, { 1, 1, 1, 1 }, {}, this->font);
label->setPos({ 4, 8 });
label->setText(title);
add(label);

View File

@ -34,7 +34,7 @@ void GuiLabelledGraph::create(glm::vec2 scale, glm::vec4 padding, const std::str
graph->setPos({ GRAPH_PAD_X, GRAPH_PAD_Y });
auto label = std::make_shared<GuiText>("label");
label->create({ 2, 2 }, {}, {}, { 1, 1, 1, 1 }, this->font);
label->create({ 2, 2 }, {}, { 1, 1, 1, 1 }, {}, this->font);
add(label);
label->setPos({ TEXT_PAD_X, TEXT_PAD_Y });

View File

@ -30,15 +30,14 @@ void GuiPerfGraph::create(f32 scale, vec4 padding, const vec<string>& sections,
meter->setPos({ GRAPH_PAD_X, GRAPH_PAD_Y });
auto label = std::make_shared<GuiText>("label");
label->create({ 2, 2 }, {}, {}, { 1, 1, 1, 1 }, this->font);
label->create({ 2, 2 }, {}, { 1, 1, 1, 1 }, {}, this->font);
label->setText(title);
add(label);
label->setPos({ TEXT_PAD_X, TEXT_PAD_Y });
for (usize i = 0; i < sections.size(); i++) {
auto label = std::make_shared<GuiText>();
label->create({ 2, 2 }, {}, vec4(GuiMeter::COLORS[i % GuiMeter::COLORS.size()], 1),
{ 1, 1, 1, 1 }, this->font);
label->create({ 2, 2 }, {}, vec4(GuiMeter::COLORS[i % GuiMeter::COLORS.size()], 1), {}, this->font);
label->setText(sections[i]);
add(label);
label->setPos({ TEXT_PAD_X + scale * (i % 2) / 2, TEXT_PAD_Y + GRAPH_PAD_Y + (i / 2) * 24 + 2 });

View File

@ -33,7 +33,7 @@ ConnectScene::ConnectScene(Client& client, Address addr) : Scene(client),
Font f(client.game->textures, client.game->textures["font"]);
auto statusText = std::make_shared<GuiText>("statusText");
statusText->create({ 2, 2 }, {}, {}, { 1, 1, 1, 1 }, f);
statusText->create({ 2, 2 }, {}, { 1, 1, 1, 1 }, {}, f);
statusText->setText("Connecting...");
statusText->setPos({ 32, 24 });
components.add(statusText);

View File

@ -34,12 +34,12 @@ MainMenuScene::MainMenuScene(Client& client) :
components->add(branding);
{
auto zephaText = make_shared<GuiText>("zephaText");
zephaText->create({ GS, GS }, {}, {}, { 1, 1, 1, 1 }, f);
zephaText->create({ GS, GS }, {}, { 1, 1, 1, 1 }, {}, f);
zephaText->setText("Zepha");
branding->add(zephaText);
auto alphaText = make_shared<GuiText>("alphaText");
alphaText->create({ GS, GS }, {}, {}, { 1, 0.5, 0.7, 1 }, f);
alphaText->create({ GS, GS }, {}, { 1, 0.5, 0.7, 1 }, {}, f);
alphaText->setText("ALPHA");
alphaText->setPos({ 25 * GS, 0 });
branding->add(alphaText);

View File

@ -7,6 +7,8 @@
#include <algorithm>
#include <stb_image/stb_image.h>
#include <cute_files/cute_files.h>
#include <util/Types.h>
#include <util/Util.h>
#include "TextureAtlas.h"
@ -181,7 +183,7 @@ std::shared_ptr<AtlasRef> TextureAtlas::generateTexture(std::string req) {
std::string paramsString = req.substr(paramsBegin + 1, paramsEnd - paramsBegin - 1);
std::vector<std::string> params;
std::string::size_type pos = 0;
std::string::size_type pos;
while ((pos = paramsString.find(',')) != std::string::npos) {
params.push_back(paramsString.substr(0, pos));
paramsString.erase(0, pos + 1);
@ -197,9 +199,22 @@ std::shared_ptr<AtlasRef> TextureAtlas::generateTexture(std::string req) {
auto data = getBytesAtPos({ src->pos.x + loc.x, src->pos.y + loc.y }, { loc.z, loc.w }).data;
return addImage(data, req, false, loc.z, loc.w);
}
else if (paramName == "multiply") {
if (params.size() != 2) throw std::runtime_error("multiply() requires 2 parameters.");
vec4 multiple = Util::hexToColorVec(params[1]);
auto tex = getBytesOfTex(params[0]);
for (int i = 0; i < tex.width * tex.height; i++) {
tex.data[i * 4 + 0] *= multiple.x;
tex.data[i * 4 + 1] *= multiple.y;
tex.data[i * 4 + 2] *= multiple.z;
tex.data[i * 4 + 3] *= multiple.w;
}
return addImage(tex.data, req, false, tex.width, tex.height);
}
else {
throw std::runtime_error("Invalid parameter.");
return nullptr;
}
}
@ -236,7 +251,6 @@ TextureAtlas::RawTexData TextureAtlas::getBytesAtPos(glm::ivec2 pos, glm::ivec2
}
data.data = pixels;
return data;
}

View File

@ -65,11 +65,14 @@ sol::table Api::Usertype::Dimension::add_entity_c(sol::this_state s, glm::vec3 p
auto displayType = luaEntity.get<sol::optional<std::string>>("display");
if (!displayType) throw std::runtime_error("entity '" + identifier + "' is missing the display property.");
auto displayObject = luaEntity.get<sol::optional<std::string>>("display_object");
if (!displayObject) throw std::runtime_error("entity '" + identifier + "' is missing the display_object property.");
auto displayObject = luaEntity.get<sol::optional<std::string>>(
(*displayType == "wireframe") ? "display_stroke" : "display_object");
if (!displayObject) throw std::runtime_error(
"entity '" + identifier + "' is missing the display_object/display_color property.");
auto displayTexture = luaEntity.get<sol::optional<std::string>>("display_texture");
if (strncmp(displayType->data(), "model", 5) == 0 && !displayTexture)
auto displayTexture = luaEntity.get<sol::optional<std::string>>(
(*displayType == "wireframe") ? "display_fill" : "display_texture");
if (displayType == "model" && !displayTexture)
throw std::runtime_error("entity '" + identifier + "' is missing the display_texture property.");
auto entity = std::make_shared<::LocalLuaEntity>(dim->getGame(), dim);
@ -105,11 +108,13 @@ sol::table Api::Usertype::Dimension::add_entity_s(sol::this_state s, glm::vec3 p
auto displayType = luaEntity.get<sol::optional<std::string>>("display");
if (!displayType) throw std::runtime_error("entity '" + identifier + "' is missing the display property.");
auto displayObject = luaEntity.get<sol::optional<std::string>>("display_object");
if (!displayObject) throw std::runtime_error("entity '" + identifier + "' is missing the display_object property.");
auto displayObject = luaEntity.get<sol::optional<std::string>>(
(*displayType == "wireframe") ? "display_color" : "display_object");
if (!displayObject) throw std::runtime_error(
"entity '" + identifier + "' is missing the display_object/display_color property.");
auto displayTexture = luaEntity.get<sol::optional<std::string>>("display_texture");
if (strncmp(displayType->data(), "model", 5) == 0 && !displayTexture)
if (displayType == "model" && !displayTexture)
throw std::runtime_error("entity '" + identifier + "' is missing the display_texture property.");
unsigned int ind = dim->nextEntityInd();

View File

@ -141,16 +141,18 @@ void Api::Usertype::Entity::snap_roll(float rot) {
entity->setRotateZ(rot);
}
float Api::Usertype::Entity::get_scale() {
return entity->getScale().x;
glm::vec3 Api::Usertype::Entity::get_scale() {
return entity->getScale();
}
void Api::Usertype::Entity::set_scale(float scale) {
entity->setScale(scale);
void Api::Usertype::Entity::set_scale(sol::object scale) {
if (scale.is<glm::vec3>()) entity->setScale(scale.as<glm::vec3>());
else entity->setScale(scale.as<f32>());
}
void Api::Usertype::Entity::snap_scale(float scale) {
entity->setScale(scale);
void Api::Usertype::Entity::snap_scale(sol::object scale) {
if (scale.is<glm::vec3>()) entity->setScale(scale.as<glm::vec3>());
else entity->setScale(scale.as<f32>());
}
Api::Usertype::Dimension Api::Usertype::Entity::get_dimension() {
@ -179,6 +181,9 @@ void Api::Usertype::Entity::set_display_type(const std::string& mode,
else if (mode == "model" && argB) {
entity->setAppearance("model", argA, *argB);
}
else if (mode == "wireframe") {
entity->setAppearance("wireframe", argA, argB ? *argB : "");
}
else throw std::runtime_error("Invalid display type parameters.");
}

View File

@ -166,7 +166,7 @@ namespace Api::Usertype {
* @returns the scale multiplier of the entity's model.
*/
float get_scale();
glm::vec3 get_scale();
/**
* Sets the scale of the entity's model,
@ -174,14 +174,14 @@ namespace Api::Usertype {
* @param scale - The desired scale multiplier.
*/
void set_scale(float scale);
void set_scale(sol::object scale);
/**
* Sets the scale of the entity's model, without any interpolation.
* @param scale - The desired scale multiplier.
*/
void snap_scale(float scale);
void snap_scale(sol::object scale);
/**
* Gets the entity's true visual-offset, which may be different from

View File

@ -80,8 +80,8 @@ sol::object LuaGuiElement::get_child(sol::this_state s, sol::object key) {
void LuaGuiElement::append(sol::this_state s, sol::object elem) {
if (elem.is<std::shared_ptr<LuaGuiElement>>()) children.push_back(elem.as<std::shared_ptr<LuaGuiElement>>());
else if (elem.is<sol::function>())
children.push_back(call(s, elem.as<sol::function>()).as<std::shared_ptr<LuaGuiElement>>());
else if (elem.is<sol::protected_function>())
children.push_back(call(s, elem.as<sol::protected_function>()).as<std::shared_ptr<LuaGuiElement>>());
else throw std::runtime_error("Append arg is not an element or a function to generate one.");
children.back()->parent = this;
@ -124,6 +124,16 @@ void LuaGuiElement::remove(sol::this_state s, sol::object elem) {
}
}
void LuaGuiElement::clear(sol::this_state s) {
for (auto it = children.cbegin(); it != children.cend();) {
(*it)->parent = nullptr;
(*it)->updateFunction = nullptr;
it = children.erase(it);
}
if (updateFunction) updateFunction();
}
Any LuaGuiElement::getAsAny(const std::string& key) const {
if (!traits.count(key)) return Any();
auto object = traits.at(key);
@ -167,4 +177,4 @@ Any LuaGuiElement::getAsAny(const std::string& key) const {
}
throw std::runtime_error("Invalid type requested in getAsAny");
}
}

View File

@ -30,6 +30,8 @@ class LuaGuiElement {
void remove(sol::this_state s, sol::object elem);
void clear(sol::this_state s);
std::string type{}, key{};
LuaGuiElement* parent = nullptr;
@ -75,7 +77,8 @@ namespace ClientApi {
"get", &LuaGuiElement::get_child,
"append", &LuaGuiElement::append,
"prepend", &LuaGuiElement::prepend,
"remove", &LuaGuiElement::remove
"remove", &LuaGuiElement::remove,
"clear", &LuaGuiElement::clear
);
}
}

View File

@ -16,6 +16,10 @@ unsigned int Api::Usertype::ServerPlayer::get_id() {
return player->getId();
}
string Api::Usertype::ServerPlayer::get_username() {
return player->getUsername();
}
glm::vec3 Api::Usertype::ServerPlayer::get_pos() {
return player->getPos();
}
@ -122,6 +126,7 @@ bool Api::Usertype::ServerPlayer::get_flying() {
void Api::Usertype::ServerPlayer::bind(State, sol::state& lua, sol::table& core) {
lua.new_usertype<ServerPlayer>("Player",
"get_id", &ServerPlayer::get_id,
"get_username", &ServerPlayer::get_username,
"get_pos", &ServerPlayer::get_pos,
"get_block_pos", &ServerPlayer::get_block_pos,
"set_pos", &ServerPlayer::set_pos,
@ -180,6 +185,7 @@ void Api::Usertype::LocalPlayer::set_hud(std::shared_ptr<LuaGuiElement> hud) {
void Api::Usertype::LocalPlayer::bind(State, sol::state& lua, sol::table& core) {
lua.new_usertype<LocalPlayer>("Player",
"get_id", &LocalPlayer::get_id,
"get_username", &LocalPlayer::get_username,
"get_pos", &LocalPlayer::get_pos,
"get_block_pos", &LocalPlayer::get_block_pos,
"set_pos", &LocalPlayer::set_pos,

View File

@ -15,75 +15,80 @@
class LuaGuiElement;
namespace Api::Usertype {
class ServerPlayer : public SubgameUsertype {
public:
ServerPlayer(PlayerPtr player) : player(player) {}
PlayerPtr player;
unsigned int get_id();
glm::vec3 get_pos();
glm::vec3 get_block_pos();
void set_pos(glm::vec3 pos);
glm::vec3 get_vel();
void set_vel(glm::vec3 vel);
float get_look_yaw();
void set_look_yaw(float rot);
float get_look_pitch();
void set_look_pitch(float rot);
sol::object get_hand_list(sol::this_state s);
sol::object get_hand_stack(sol::this_state s);
void set_hand_list(sol::optional<sol::object> list);
sol::object get_wield_list(sol::this_state s);
sol::object get_wield_stack(sol::this_state s);
void set_wield_list(sol::optional<sol::object> list);
Inventory get_inventory();
Dimension get_dimension();
void set_dimension(const std::string& identifier);
unsigned int get_wield_index();
void set_wield_index(unsigned int index);
void set_flying(bool shouldFly);
bool get_flying();
static void bind(State state, sol::state& lua, sol::table& core);
};
class ServerPlayer;
class LocalPlayer;
}
class Api::Usertype::ServerPlayer : public SubgameUsertype {
public:
ServerPlayer(PlayerPtr player) : player(player) {}
class LocalPlayer : public ServerPlayer {
public:
LocalPlayer(PlayerPtr player) : ServerPlayer(player) {}
bool is_in_menu();
void show_menu(std::shared_ptr<LuaGuiElement> root);
void close_menu();
std::shared_ptr<LuaGuiElement> get_hud();
void set_hud(std::shared_ptr<LuaGuiElement> hud);
static void bind(State state, sol::state& lua, sol::table& core);
};
}
PlayerPtr player;
unsigned int get_id();
string get_username();
glm::vec3 get_pos();
glm::vec3 get_block_pos();
void set_pos(glm::vec3 pos);
glm::vec3 get_vel();
void set_vel(glm::vec3 vel);
float get_look_yaw();
void set_look_yaw(float rot);
float get_look_pitch();
void set_look_pitch(float rot);
sol::object get_hand_list(sol::this_state s);
sol::object get_hand_stack(sol::this_state s);
void set_hand_list(sol::optional<sol::object> list);
sol::object get_wield_list(sol::this_state s);
sol::object get_wield_stack(sol::this_state s);
void set_wield_list(sol::optional<sol::object> list);
Inventory get_inventory();
Dimension get_dimension();
void set_dimension(const std::string& identifier);
unsigned int get_wield_index();
void set_wield_index(unsigned int index);
void set_flying(bool shouldFly);
bool get_flying();
static void bind(State state, sol::state& lua, sol::table& core);
};
class Api::Usertype::LocalPlayer : public ServerPlayer {
public:
LocalPlayer(PlayerPtr player) : ServerPlayer(player) {}
bool is_in_menu();
void show_menu(std::shared_ptr<LuaGuiElement> root);
void close_menu();
std::shared_ptr<LuaGuiElement> get_hud();
void set_hud(std::shared_ptr<LuaGuiElement> hud);
static void bind(State state, sol::state& lua, sol::table& core);
};

View File

@ -32,7 +32,7 @@ Server::Server(u16 port, const std::string& subgame) :
void Server::update() {
const static i64 interval_ns = static_cast<i64>((1000 / 60.f) * 1000000L);
Timer loop("");
world.s()->update(delta);
game.s()->update(delta);
@ -58,9 +58,8 @@ void Server::update() {
PacketView p(event.packet);
auto client = static_cast<ServerClient*>(event.peer->data);
if (client->player) return playerPacketReceived(p, clients.getClient(client->id)->player);
if (infoSender.handlePacket(*client, p)) {
if (client->player) playerPacketReceived(p, clients.getClient(client->id)->player);
else if (infoSender.handlePacket(*client, p)) {
auto clientShr = clients.getClient(client->id);
clients.createPlayer(clientShr, world->getDefaultDimension());
}
@ -93,7 +92,7 @@ void Server::update() {
playersUpdated.clear();
u64 sleep_for = (std::max)(interval_ns - static_cast<i64>(loop.elapsedNs()), 0L);
i64 sleep_for = (std::max)(interval_ns - static_cast<i64>(loop.elapsedNs()), 0L);
if (sleep_for > 0) std::this_thread::sleep_for(std::chrono::nanoseconds(sleep_for));
delta = loop.elapsedNs() / 1000000.f / 1000.f;
@ -164,6 +163,7 @@ void Server::playerPacketReceived(PacketView& p, sptr<ServerPlayer> player) {
}
case Packet::Type::INV_INTERACT: {
// TODO: When reading these parameters, there's a bad_alloc error.
bool primary = p.d.read<bool>();
string source = p.d.read<string>();
string list = p.d.read<string>();
@ -174,9 +174,10 @@ void Server::playerPacketReceived(PacketView& p, sptr<ServerPlayer> player) {
case Packet::Type::MOD_MESSAGE: {
string source = p.d.read<string>();
string list = p.d.read<string>();
string message = p.d.read<string>();
game->getParser().safe_function(game->getParser().core["trigger"], "message",
source, game->getParser().safe_function(game->getParser().core["deserialize"], list));
source, game->getParser().safe_function(game->getParser().core["deserialize"], message),
Api::Usertype::ServerPlayer(player));
break;
}
}

View File

@ -16,7 +16,6 @@ ServerGenStream::ServerGenStream(ServerSubgame& game, ServerWorld& world) :
bool ServerGenStream::queue(u16 dimension, ivec3 pos) {
auto v4 = ivec4(pos, dimension);
// if (!queuedMap.count(v4)) {
if (queuedMap.find(v4) == queuedMap.end() && inProgressMap.find(v4) == inProgressMap.end()) {
queuedTasks.push(v4);
queuedMap.emplace(v4);
@ -28,6 +27,7 @@ bool ServerGenStream::queue(u16 dimension, ivec3 pos) {
uptr<vec<ServerGenStream::FinishedJob>> ServerGenStream::update() {
auto created = make_unique<vec<FinishedJob>>();
usize dequeued = 0;
for (usize i = 0; i < THREAD_QUEUE_SIZE; i++) {
for (auto& t : threads) {
auto& j = t.jobs[i];
@ -43,6 +43,7 @@ uptr<vec<ServerGenStream::FinishedJob>> ServerGenStream::update() {
queuedMap.erase(pos);
inProgressMap.emplace(pos);
queuedTasks.pop();
dequeued++;
j.pos = ivec3(pos);
j.gen = world.getDimension(pos.w)->getGen();
@ -52,6 +53,9 @@ uptr<vec<ServerGenStream::FinishedJob>> ServerGenStream::update() {
}
}
if (dequeued >= THREAD_QUEUE_SIZE * THREADS)
std::cout << Log::err << "Server threads were idle!" << Log::endl;
return created;
}

View File

@ -18,7 +18,7 @@ class ServerSubgame;
class ServerGenStream {
public:
static const usize THREADS = 4;
static const usize THREAD_QUEUE_SIZE = 16;
static const usize THREAD_QUEUE_SIZE = 4;
struct FinishedJob {
FinishedJob(u16 dim, ivec3 pos, uptr<MapGen::ChunkMap> created) :
@ -37,13 +37,13 @@ public:
std::unique_ptr<std::vector<FinishedJob>> update();
private:
private:
struct Job {
bool locked = false;
uptr<MapGen::ChunkMap> created = nullptr;
u16 dim;
ivec3 pos{};
ivec3 pos {};
sptr<MapGen> gen;
};

View File

@ -54,6 +54,6 @@ void Bounds::setB(glm::vec3 vecB) {
bool Bounds::intersects(glm::vec3 test) {
return (test.x >= a.x && test.x <= b.x
&& test.y >= a.y && test.y <= b.y
&& test.z >= a.z && test.z <= b.z);
&& test.y >= a.y && test.y <= b.y
&& test.z >= a.z && test.z <= b.z);
}

View File

@ -38,8 +38,9 @@ namespace Util {
return toFixed<T>(val);
}
template <typename V, std::enable_if_t<std::is_trivially_copyable_v<typename V::value_type> &&
std::is_same_v<vec<typename V::value_type>, V>, bool> = true>
template <typename V, std::enable_if_t<(std::is_trivially_copyable_v<typename V::value_type>
|| std::is_same_v<typename V::value_type, string>)
&& std::is_same_v<vec<typename V::value_type>, V>, bool> = true>
static string toString(V vec) {
std::ostringstream out;
out << "[ ";
@ -48,8 +49,9 @@ namespace Util {
return out.str();
}
template <typename A, std::enable_if_t<std::is_trivially_copyable_v<typename A::value_type> &&
std::is_same_v<array<typename A::value_type, A::size_type>, A>, bool> = true>
template <typename A, std::enable_if_t<(std::is_trivially_copyable_v<typename A::value_type>
|| std::is_same_v<typename A::value_type, string>)
&& std::is_same_v<array<typename A::value_type, A::size_type>, A>, bool> = true>
static string toString(A arr) {
std::ostringstream out;
for (usize i = 0; i < arr.size(); i++) out << (i == 0 ? "" : ", ") << arr[i];
@ -122,7 +124,7 @@ namespace Util {
color.g = intFromHexSegment(g) / 255.f;
color.b = intFromHexSegment(b) / 255.f;
color.a = intFromHexSegment(a) / 255.f;
return color;
}

View File

@ -66,23 +66,29 @@ void ServerWorld::update(f64 delta) {
refs->update();
u32 genCount = 0;
std::unordered_set<ivec4, Vec::ivec4> updatedChunks {};
auto finishedGen = genStream->update();
std::unordered_set<ivec4, Vec::ivec4> chunksDirtied {};
Timer t("Finishing Generation");
for (auto& data : *finishedGen) {
let dim = getDimension(data.dim);
for (const auto& chunkPair : *data.created) {
updatedChunks.insert(ivec4(chunkPair.first, data.dim));
dim->setChunk(chunkPair.second);
let chunkMapBlockPos = Space::MapBlock::world::fromChunk(chunkPair.first);
if (chunkMapBlockPos != data.pos) {
let chunkMapBlock = dim->getMapBlock(chunkMapBlockPos);
if (chunkMapBlock->generated) chunksDirtied.insert(ivec4(chunkPair.first, data.dim));
}
}
auto mapBlock = dim->getMapBlock(ivec3(data.pos));
let mapBlock = dim->getMapBlock(ivec3(data.pos));
assert(mapBlock);
if (!mapBlock->generated) {
mapBlock->generated = true;
assert(mapBlock);
Serializer s {};
for (u16 i = 0; i < 64; i++) {
@ -102,50 +108,32 @@ void ServerWorld::update(f64 delta) {
totalGens++;
}
}
if (!finishedGen->empty()) {
t.printElapsedMs();
std::cout << totalGens << std::endl;
}
// auto finishedPackets = packetStream->update();
// if (finishedPackets->size()) std::cout << finishedPackets->size() << " finished packets" << std::endl;
// for (auto& data : *finishedPackets) {
// for (auto& client : clients.getClients()) {
// if (!client.second->player) continue;
// data->packet->sendTo(client.second->peer, Packet::Channel::WORLD);
// }
// }
generatedMapBlocks = genCount;
// for (auto& chunkPos : updatedChunks) {
// std::cout << Util::toString(chunkPos) << std::endl;
// ivec3 mapBlockPos = Space::MapBlock::world::fromChunk(chunkPos);
// auto dim = getDimension(chunkPos.w);
// auto chunk = dim->getChunk(ivec3(chunkPos));
//
// assert(chunk != nullptr);
//
//// bool sentAlready = false;
//// for (auto& mapBlock : generatedMapBlocks) if (mapBlock == mapBlockPos) { sentAlready = true; break; }
//// if (sentAlready) continue;
//
// Packet p(Packet::Type::CHUNK);
// p.data = chunk->compress();
//
// for (auto& client : clients.getClients()) {
// if (!client.second->player) continue;
//
for (auto& chunkPos : chunksDirtied) {
ivec3 mapBlockPos = Space::MapBlock::world::fromChunk(chunkPos);
auto dim = getDimension(chunkPos.w);
auto chunk = dim->getChunk(ivec3(chunkPos));
assert(chunk);
Packet p(Packet::Type::CHUNK);
p.data = chunk->compressToString();
for (auto& client : clients.getClients()) {
if (!client.second->player) continue;
// std::cout << "dirtied" << chunk->getPos() << std::endl;
// auto myChunk = Space::Chunk::world::fromBlock(client.second->player->getPos());
//
// std::pair<ivec3, ivec3> bounds = {
// {myChunk.x - activeChunkRange.x, myChunk.y - activeChunkRange.y, myChunk.z - activeChunkRange.x},
// {myChunk.x + activeChunkRange.x, myChunk.y + activeChunkRange.y, myChunk.z + activeChunkRange.x}};
//
//// if (isInBounds(chunkPos, bounds))
// p.sendTo(client.second->peer, Packet::Channel::WORLD);
// }
// }
// if (isInBounds(chunkPos, bounds))
p.sendTo(client.second->peer, Packet::Channel::WORLD);
}
}
Packet r = Serializer().append(generatedMapBlocks).packet(Packet::Type::SERVER_INFO);
@ -178,7 +166,6 @@ void ServerWorld::update(f64 delta) {
}
// Update clients with removed entities.
Serializer rem;
rem.append(ind);
for (i64 entity : dimension->getRemovedEntities()) rem.append<i64>(entity);
@ -240,32 +227,29 @@ void ServerWorld::sendChunksToPlayer(ServerPlayer& client) {
ivec3 playerPos = Space::MapBlock::world::fromBlock(client.getPos());
ivec3 lastPlayerPos = Space::MapBlock::world::fromBlock(client.lastPos);
Bounds newBounds = { playerPos - ivec3{ sendRange.x, sendRange.y, sendRange.x },
playerPos + ivec3{ sendRange.x, sendRange.y, sendRange.x }};
Bounds oldBounds = { lastPlayerPos - ivec3{ sendRange.x, sendRange.y, sendRange.x },
lastPlayerPos + ivec3{ sendRange.x, sendRange.y, sendRange.x }};
Bounds newBounds = { playerPos - ivec3 { sendRange.x, sendRange.y, sendRange.x },
playerPos + ivec3 { sendRange.x, sendRange.y, sendRange.x }};
Bounds oldBounds = { lastPlayerPos - ivec3 { sendRange.x, sendRange.y, sendRange.x },
lastPlayerPos + ivec3 { sendRange.x, sendRange.y, sendRange.x }};
for (auto& pos : generateOrder) {
if (oldBounds.intersects(playerPos + pos) || !newBounds.intersects(playerPos + pos)) continue;
// packetStream->queue(client.getDim()->getInd(), pos + playerPos);
// auto dim = client.getDim();
// std::cout << dim->getInd() << std::endl;
// auto mb = dim->getMapBlock(pos);
// std::cout << mb << std::endl;
// if (!mb) return;
// Serializer s {};
// for (u16 i = 0; i < 64; i++) {
// auto chunk = mb->get(i);
// if (chunk) s.append(chunk->compressToString());
// }
//
// let packet = make_unique<Packet>(Packet::Type::MAPBLOCK);
//
// for (auto& client : clients.getClients()) {
// if (!client.second->player) continue;
// packet->sendTo(client.second->peer, Packet::Channel::WORLD);
// }
auto dim = client.getDim();
auto mb = dim->getMapBlock(playerPos + pos);
if (!mb) return;
// std::cout << "sending " << pos << " to " << client.getId() << std::endl;
Serializer s {};
for (u16 i = 0; i < 64; i++) {
auto chunk = mb->get(i);
if (chunk) s.append(chunk->compressToString());
}
let packet = s.packet(Packet::Type::MAPBLOCK);
for (auto& client : clients.getClients()) {
if (!client.second->player) continue;
packet.sendTo(client.second->peer, Packet::Channel::WORLD);
}
}
}

View File

@ -83,6 +83,13 @@ void LocalDimension::update(f64 delta) {
}
void LocalDimension::setChunk(sptr<Chunk> chunk) {
let clientMapBlock = Space::MapBlock::world::fromBlock(static_cast<LocalWorld&>(world).getPlayer()->getPos());
let mapBlockPos = Space::MapBlock::world::fromChunk(chunk->getPos());
if (abs(clientMapBlock.x - mapBlockPos.x) > retainMapBlockRange.x ||
abs(clientMapBlock.y - mapBlockPos.y) > retainMapBlockRange.y ||
abs(clientMapBlock.z - mapBlockPos.z) > retainMapBlockRange.x) return;
Dimension::setChunk(chunk);
meshChunk(chunk);
}

View File

@ -53,7 +53,7 @@ private:
std::list<Api::Usertype::Entity> luaEntities{};
std::list<i64> removedEntities{};
const ivec2 retainMapBlockRange = { 4, 4 };
const ivec2 retainMapBlockRange = { 6, 6 };
i64 entityInd = 1;
};

View File

@ -65,7 +65,6 @@ void Chunk::combineWith(sptr<Chunk> o) {
generationState = GenerationState::GENERATED;
countRenderableBlocks();
}
else generationState = GenerationState::PARTIAL;
}

View File

@ -95,7 +95,6 @@ void DrawableEntity::interpScale(float scale) {
void DrawableEntity::setScale(glm::vec3 scale) {
visualScale = scale;
Entity::setScale(scale);
}
void DrawableEntity::interpScale(glm::vec3 scale) {

View File

@ -12,7 +12,21 @@
LocalLuaEntity::LocalLuaEntity(SubgamePtr game, DimensionPtr dim) :
LuaEntity(game, dim), DrawableEntity(game, dim), Entity(game, dim) {}
void LocalLuaEntity::setAppearance(const std::string& mode, const std::string& argA, const std::string& argB) {
void LocalLuaEntity::setScale(f32 newScale) {
if (appearance == "wireframe") model->fromMesh(
WireframeEntity::createMesh({ {}, vec3(newScale) }, 0.035, wfStroke, wfFill));
else DrawableEntity::setScale(scale);
}
void LocalLuaEntity::setScale(vec3 newScale) {
if (appearance == "wireframe") model->fromMesh(
WireframeEntity::createMesh({ {}, newScale }, 0.035, wfStroke, wfFill));
else DrawableEntity::setScale(scale);
}
void LocalLuaEntity::setAppearance(const string& mode, const string& argA, const string& argB) {
this->appearance = mode;
if (mode == "gameobject" && (argA == "block" || argA == "craftitem")) {
ItemDef& def = getGame()->getDefs().fromStr(argB);
setModel(def.entityModel);
@ -22,5 +36,12 @@ void LocalLuaEntity::setAppearance(const std::string& mode, const std::string& a
model->fromSerialized(getGame().l()->models.models[argA], { getGame().l()->textures[argB] });
setModel(model);
}
else if (mode == "wireframe") {
wfStroke = Util::hexToColorVec(argA);
wfFill = argB.empty() ? vec4() : Util::hexToColorVec(argB);
auto model = std::make_shared<Model>();
model->fromMesh(WireframeEntity::createMesh({ {}, scale }, 0.035, wfStroke, wfFill));
setModel(model);
}
else throw std::runtime_error("Invalid appearance arguments specified.");
}
}

View File

@ -11,5 +11,14 @@ class LocalLuaEntity : public virtual DrawableEntity, public LuaEntity {
public:
LocalLuaEntity(SubgamePtr game, DimensionPtr dim);
virtual void setScale(float newScale) override;
virtual void setScale(vec3 newScale) override;
virtual void setAppearance(const std::string& dMode, const std::string& argA, const std::string& argB) override;
protected:
string appearance;
vec3 wfStroke;
vec4 wfFill;
};

View File

@ -35,7 +35,8 @@ MapGen::MapGen(Subgame& game, World& world, u32 seed, std::unordered_set<string>
let biomeFractal = FastNoise::New<FastNoise::FractalFBm>();
biomeFractal->SetSource(biomeScale);
biomeFractal->SetOctaveCount(4);
biomeFractal->SetOctaveCount(5);
biomeFractal->SetLacunarity(3);
biomeGenerator = biomeFractal;
}

View File

@ -3,8 +3,8 @@
#include "NoiseSample.h"
NoiseSample::NoiseSample(u16vec2 size, u16 precision) : NoiseSample({ size.x, 0, size.y }, precision) {}
NoiseSample::NoiseSample(u16vec2 size, u16 precision):
NoiseSample({ size.x, 0, size.y }, precision) {}
NoiseSample::NoiseSample(u16vec3 size, u16 precision):
size(size), precision(precision), data((size.x + 1) * (size.y + 1) * (size.z + 1), 0) {
@ -23,7 +23,7 @@ void NoiseSample::generate(ivec3 pos, const FastNoise::SmartNode<>& generator) {
interpolateData();
}
void NoiseSample::fill(const NoiseSample::fill_function& fill) {
void NoiseSample::fill(const std::function<f32(ivec3)>& fill) {
u16vec3 pos {};
for (pos.x = 0; pos.x < size.x + 1; pos.x += precision)
for (pos.y = 0; pos.y < size.y + 1; pos.y += precision)
@ -41,8 +41,8 @@ void NoiseSample::interpolateData() {
if (pos.x % precision == 0 && pos.y % precision == 0 && pos.z % precision == 0) continue;
vec3 frac = vec3(pos) / static_cast<f32>(precision);
u16vec3 a = u16vec3(glm::floor(frac)) * static_cast<u16>(precision);
u16vec3 b = u16vec3(glm::ceil(frac)) * static_cast<u16>(precision);
u16vec3 a = u16vec3(glm::floor(frac)) * precision;
u16vec3 b = u16vec3(glm::ceil(frac)) * precision;
vec3 factor = frac - glm::floor(frac);
data[index(pos)] = Interp::trilerp(

View File

@ -6,67 +6,49 @@
#include "util/Types.h"
#include "util/Interp.h"
/**
* Stores a 2d or 3d array of noise data that can be accessed by local position or index.
* Noise will only be sampled every `precision` blocks, and interpolated between them.
* Generates 1 block larger on each access due to interpolation, but the index signature
* will access it as though it was exactly `size` blocks wide.
*/
class NoiseSample {
public:
typedef std::function<f32(ivec3 pos)> fill_function;
NoiseSample(u16vec2 size, u16 precision);
NoiseSample(u16vec3 size, u16 precision);
/** Populates the data with a noise generator. */
void generate(ivec3 pos, const FastNoise::SmartNode<>& generator);
void fill(const fill_function& fill);
/** Populates the data with a fill function. */
void fill(const std::function<f32(ivec3)>& fill);
/** Retrieves the value from a local position. */
inline f32 operator[](u16vec3 pos) {
return data[index(pos)];
}
/** Retrieves the value from an index into a `size`-sized cuboid. */
inline f32 operator[](u32 ind) {
return data[shiftIndexByOne(ind)];
}
// inline f32 get(vec3 pos) {
// vec3 scaled = pos * vec3(precision) / scaleBy;
//
// ivec3 a = { scaled.x, scaled.y, scaled.z };
// vec3 factor = { scaled.x - a.x, scaled.y - a.y, scaled.z - a.z };
// ivec3 b = {
// (std::min)(static_cast<i32>(std::ceil(scaled.x)), precision.x),
// (std::min)(static_cast<i32>(std::ceil(scaled.y)), precision.y),
// (std::min)(static_cast<i32>(std::ceil(scaled.z)), precision.z) };
//
// assert(index(b.x, b.y, b.z) < data.size());
//
// // No vertical interpolation
// if (precision.y == 0)
// return Interp::bilerp(
// data[index(a.x, a.y, a.z)], data[index(b.x, a.y, a.z)],
// data[index(a.x, a.y, b.z)], data[index(b.x, a.y, b.z)],
// factor.x, factor.z);
//
// return Interp::trilerp(
// data[index(a.x, a.y, a.z)], data[index(b.x, a.y, a.z)],
// data[index(a.x, a.y, b.z)], data[index(b.x, a.y, b.z)],
// data[index(a.x, b.y, a.z)], data[index(b.x, b.y, a.z)],
// data[index(a.x, b.y, b.z)], data[index(b.x, b.y, b.z)],
// factor.x, factor.z, factor.y);
// }
private:
/** Interpolates the data between sampled points. */
void interpolateData();
// inline u32 innerIndex(u16 x, u16 y, u16 z) {
// return static_cast<u32>(x) * size.x * size.y + y * size.x + z;
// };
/** Retrieves the index for a position vector. */
inline u32 index(u16vec3 pos) {
return index(pos.x, pos.y, pos.z);
};
/** Retrieves the index for the inner data from position coordinates. */
inline u32 index(u16 x, u16 y, u16 z) {
return static_cast<u32>(x) * (size.x + 1) * (size.y + 1) + y * (size.x + 1) + z;
};
/** Shifts an index value from the provided `size` to the internal dimensions. */
inline u32 shiftIndexByOne(u32 ind) {
u16vec3 vec = {};
@ -80,8 +62,13 @@ private:
return index(vec);
};
/** The specified size of the Sample. The actual size is 1 larger on each axis. */
u16vec3 size;
/** The precision at which points are sampled. */
u16 precision;
/** The point data. */
vec<f32> data {};
};

View File

@ -47,6 +47,10 @@ void LocalPlayer::update(Input& input, f64 delta, vec2 mouseDelta) {
if (!gameGui.isInMenu()) updateInteract(input, delta);
}
string LocalPlayer::getUsername() {
return "UNIMPLEMENTED";
}
void LocalPlayer::setPos(vec3 pos, bool assert) {
Player::setPos(pos, assert);
renderer.camera.setPos(pos + getLookOffset());
@ -447,4 +451,4 @@ void LocalPlayer::updateInteract(Input& input, f64 delta) {
dim->wieldItemUse(target, static_cast<LocalWorld&>(dim->getWorld()).getPlayer());
}
}
}
}

View File

@ -47,6 +47,8 @@ public:
void update(Input& input, f64 delta, vec2 mouseDelta);
virtual string getUsername() override;
/** The following setters call Player setters, but updates some rendering-related information. */
virtual void setPos(vec3 pos, bool assert = false) override;

View File

@ -22,6 +22,8 @@ public:
this->id = id;
}
virtual string getUsername() = 0;
virtual void setDim(DimensionPtr dim, bool assert = false);
virtual void setPos(vec3 pos, bool assert = false);

View File

@ -14,6 +14,10 @@ ServerPlayer::ServerPlayer(ServerClient& client, World& world, SubgamePtr game,
inventory->createList("cursor", 1, 1);
}
string ServerPlayer::getUsername() {
return client.username;
}
void ServerPlayer::assertField(Packet packet) {
packet.type = Packet::Type::THIS_PLAYER_INFO;
packet.sendTo(getPeer(), Packet::Channel::INTERACT);
@ -90,4 +94,4 @@ InventoryPtr ServerPlayer::getInventory() {
ENetPeer* ServerPlayer::getPeer() {
return client.peer;
}
}

View File

@ -23,6 +23,8 @@ class ServerPlayer : public Player {
public:
ServerPlayer(ServerClient& client, World& world, SubgamePtr game, DimensionPtr dim);
virtual string getUsername() override;
virtual void assertField(Packet packet) override;
virtual void handleAssertion(Deserializer& d) override;

View File

@ -0,0 +1,5 @@
{
"name": "@auri:chat",
"description": "A simple chat interface and API supporting messaging and commands.",
"version": "0.0.1"
}

View File

@ -0,0 +1,8 @@
-- The global chat object.
_G['chat'] = {
channels = {},
channel_order = {},
}
if zepha.server then runfile(_PATH .. 'api_server')
else runfile(_PATH .. 'api_client') end

View File

@ -0,0 +1,58 @@
chat.open = false
chat.current_channel = nil
chat._message_persist_time = 5
-- Toggles the chat being open and listening for input.
chat.set_open = function(open)
if open == nil then chat.open = not chat.open
else chat.open = open end
chat._refresh()
end
-- Sends a message from the player to the chat channel.
chat.send = function(message)
chat.send_channel(nil, message)
end
-- Sends a message from player to the channel provided.
chat.send_channel = function(channel_name, message)
zepha.send_message('@auri:chat:message', {
channel = channel_name,
content = message
})
end
local function add_channel(channel)
print(channel.identifier)
chat.channels[channel.identifier] = {
name = channel.name,
icon = channel.icon,
history = {},
_current_history_head = 1
}
chat.channel_order = channel.order
if chat.current_channel == nil then chat.current_channel = channel.identifier end
chat._refresh()
end
local function add_message(message)
local channel = chat.channels[message.channel]
if not channel then return end
table.insert(channel.history, {
from = message.from,
content = message.content,
time = zepha.time.s()
})
chat._refresh()
zepha.after(chat._refresh, chat._message_persist_time)
end
zepha.bind('message', function(channel, message)
if not channel:starts_with('@auri:chat') then return end
if channel == '@auri:chat:new_channel' then add_channel(message) end
if channel == '@auri:chat:message' then add_message(message) end
end)

View File

@ -0,0 +1,63 @@
-- Creates a new chat channel with the name and options provided.
chat.create_channel = function(identifier, options)
chat.channels[identifier] = {
name = options.name or identifier,
icon = options.icon,
history = {},
}
table.insert(chat.channel_order, identifier)
zepha.send_message('@auri:chat:new_channel', {
identifier = identifier,
name = options.name or identifier,
icon = options.icon,
order = chat.channel_order
})
end
-- Sends a message from the sender specified to the chat channel.
chat.send = function(sender, message)
chat.send_channel(sender, nil, message)
end
-- Sends a message from the sender specified to the channel provided.
chat.send_channel = function(sender, channel_name, message)
if not channel_name then channel_name = 'chat' end
local channel = chat.channels[channel_name]
if not channel then return end
table.insert(channel.history, {
from = sender,
content = message,
time = zepha.time.s()
})
zepha.send_message('@auri:chat:message', {
channel = channel_name,
from = sender,
content = message
})
end
zepha.bind('player_join', function(player)
for _, identifier in ipairs(chat.channel_order) do
local channel = chat.channels[identifier]
zepha.send_message('@auri:chat:new_channel', {
identifier = identifier,
name = channel.name or identifier,
icon = channel.icon,
order = chat.channel_order
})
end
end)
zepha.bind('message', function(channel, message, player)
if not channel:starts_with('@auri:chat') then return end
if channel == '@auri:chat:message' then
local user = player:get_username()
local color = user:sub(16, 16)
chat.send_channel('`b`c' .. color .. user, message.channel, message.content)
end
end)

Binary file not shown.

View File

@ -0,0 +1,102 @@
local max_messages = 8
local chat_wrap = zepha.build_gui(function()
return Gui.Rect {
position = { 4, '100%' },
position_anchor = { 0, '200%' },
size = { 256, 23 + max_messages * 8 },
Gui.Rect {
key = 'chat_tabs',
position = { 0, -10 }
},
Gui.Rect {
key = 'chat_box',
size = { 256, 2 + max_messages * 8 }
},
Gui.Rect {
key = 'chat_input',
size = { 256, 10 },
position = { 0, 3 + max_messages * 8 }
}
}
end)
local chat_box = chat_wrap:get('chat_box')
local chat_tabs = chat_wrap:get('chat_tabs')
local chat_input = chat_wrap:get('chat_input')
-- Rerenders the chat gui.
chat._refresh = function()
chat_box:clear()
chat_tabs:clear()
if chat.open then
local i = 0
for _, identifier in ipairs(chat.channel_order) do
local channel = chat.channels[identifier]
chat_tabs:append(function()
return Gui.Rect {
size = { 48, 10 },
position = { i * 49, 0 },
background = (chat.current_channel == identifier) and '#0005' or '#0002',
Gui.Rect {
position = { 2, 2 },
size = { 8 * (2/3), 8 * (2/3) },
background = 'multiply(' .. channel.icon .. ', ' ..
(chat.current_channel == identifier and '#ffe791' or '#fff') .. ')'
},
Gui.Text {
color = (chat.current_channel == identifier) and '#ffe791' or '#fff',
position = { 8, 2 },
scale = { 2.02/3, 2.02/3 },
content = channel.name
}
}
end)
i = i + 1
end
end
local channel = chat.channels[chat.current_channel]
if not channel then return end
local now = zepha.time.s()
local count = 0
local start = math.max(chat.open and 1 or channel._current_history_head, #channel.history - max_messages + 1)
for i = #channel.history, start, -1 do
local message = channel.history[i]
if now - message.time > chat._message_persist_time and not chat.open then
channel._current_history_head = i + 1
else
chat_box:append(function()
return Gui.Text {
position = { 2, 2 + (max_messages - count - 1) * 8 },
background = chat.open and '#0000' or '#0003',
scale = { 2.02/3, 2.02/3 },
content = '`c7[` `r' .. message.from .. '`r` `c7]`cr ' .. message.content .. '`r'
}
end)
count = count + 1
end
end
chat_box.background = chat.open and '#0005' or '#0000'
chat_input.background = chat.open and '#0005' or '#0000'
end
zepha.player:get_hud():append(chat_wrap)
chat._refresh()
-- Keyboard shortcut to toggle the chat.
zepha.register_keybind(":open_chat", {
description = "Open Chat",
default = zepha.keys.p,
on_press = chat.set_open
})

View File

@ -0,0 +1,8 @@
runfile(_PATH .. 'api')
if zepha.client then runfile(_PATH .. 'gui') end
if zepha.server then
chat.create_channel('chat', { name = 'Chat', icon = '@auri:chat:chat_icon' })
end
runfile(_PATH .. 'test')

View File

@ -0,0 +1,29 @@
if zepha.server then
chat.create_channel('commands', { name = 'Commands', icon = '@auri:chat:commands_icon' })
chat.create_channel('debug', { name = 'Debug', icon = '@auri:chat:debug_icon' })
--
-- local random_messages = {
-- { '`b`cbA`cdu`cfr`cdi`cb!', 'the quick brown fox jumps over the lazy dog' },
-- { '`b`cbA`cdu`cfr`cdi`cb!', 'lorum ipsum dolor sit amet consequitor lorem ipsum dolor sit amet' },
-- { '`b`cbZ`cay`cet`cch`cdi`c5a', '`iomg shut the fuck up`r' },
-- { '`b`cbA`cdu`cfr`cdi`cb!', 'lol epic' },
-- { '`b`cbA`cdu`cfr`cdi`cb!', 'chat mod' },
-- { '`b`cbZ`cay`cet`cch`cdi`c5a', 'testing testing 123' },
-- { '`bJAYCEE', 'PENIS!' },
-- { '`b`cbA`cdu`cfr`cdi`cb!', '#epic' },
-- { 'alphabet', '`bABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789' }
-- }
--
-- local function random_message()
-- local message = random_messages[math.floor(math.random() * #random_messages) + 1]
-- chat.send(message[1], message[2])
-- zepha.after(random_message, math.random() > 0.5 and math.random() * 1 or math.random() * 2)
-- end
-- random_message()
elseif zepha.client then
zepha.register_keybind(":send_test_message", {
description = "Send test message",
default = zepha.keys.j,
on_press = function() chat.send('hihihih') end
})
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

View File

@ -0,0 +1,48 @@
local box = nil
local pos1 = nil
local pos2 = nil
zepha.register_entity('@auri:world_edit:box', {
display = 'wireframe',
display_stroke = '#0e7',
update_pos = function(self)
if pos2 == nil then
self.object.pos = pos1
self.object.scale = 1
else
local min = V(math.min(pos1.x, pos2.x), math.min(pos1.y, pos2.y), math.min(pos1.z, pos2.z))
local max = V(math.max(pos1.x, pos2.x), math.max(pos1.y, pos2.y), math.max(pos1.z, pos2.z))
local scale = max - min + 1
self.object.pos = min
self.object.scale = scale
end
end
})
zepha.register_item(':wand', {
name = 'World Edit Wand',
textures = { '@auri:world_edit:wand' },
stack = 1,
on_use_client = function(stack, target, player)
if box == nil then
box = zepha.player.dim:add_entity(V(), '@auri:world_edit:box')
end
if pos1 == nil or pos2 ~= nil then
pos2 = nil
pos1 = V(target.pos or player.pos):floor()
else
pos2 = V(target.pos or player.pos):floor()
end
box:update_pos()
end
})
zepha.bind('new_player', function(player)
local inv = player:get_inventory():get_list('hot_wheel_1')
inv:add_stack({ '@auri:world_edit:wand', 1 })
end)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -1,104 +1,105 @@
local identifier = "zeus:world:desert"
local structures = {}
table.insert(structures, zepha.create_structure({
origin = V(),
probability = 0.001,
layout = {{{ "zeus:default:cactus" }}, {{ "zeus:default:cactus" }}}
}))
table.insert(structures, zepha.create_structure({
origin = V(),
probability = 0.001,
layout = {{{ "zeus:default:cactus" }}, {{ "zeus:default:cactus" }}, {{ "zeus:default:cactus" }}}
}))
table.insert(structures, zepha.create_structure({
origin = V(),
probability = 0.001,
layout = {{{ "zeus:default:cactus" }}, {{ "zeus:default:cactus" }}, {{ "zeus:default:cactus" }}, {{ "zeus:default:cactus" }}}
}))
local noise = {
-- heightmap = {
-- module = "add",
-- sources = {{
-- module = "min",
-- sources = {{
-- module = "turbulence",
-- power = 1,
-- frequency = 0.3,
-- local identifier = "zeus:world:desert"
--
-- local structures = {}
--
-- table.insert(structures, zepha.create_structure({
-- origin = V(),
-- probability = 0.001,
-- layout = {{{ "zeus:default:cactus" }}, {{ "zeus:default:cactus" }}}
-- }))
--
-- table.insert(structures, zepha.create_structure({
-- origin = V(),
-- probability = 0.001,
-- layout = {{{ "zeus:default:cactus" }}, {{ "zeus:default:cactus" }}, {{ "zeus:default:cactus" }}}
-- }))
--
-- table.insert(structures, zepha.create_structure({
-- origin = V(),
-- probability = 0.001,
-- layout = {{{ "zeus:default:cactus" }}, {{ "zeus:default:cactus" }}, {{ "zeus:default:cactus" }}, {{ "zeus:default:cactus" }}}
-- }))
--
-- local noise = {
-- -- heightmap = runfile(_PATH .. 'world_noise')
-- -- heightmap = {
-- -- module = "add",
-- -- sources = {{
-- -- module = "min",
-- -- sources = {{
-- -- module = "turbulence",
-- -- power = 1,
-- -- frequency = 0.3,
-- -- source = {
-- -- module = "add",
-- -- sources = {{
-- -- -- Elevation
-- -- module = "scale_bias",
-- -- source = {
-- -- module = "spheres",
-- -- frequency = 0.2
-- -- },
-- -- scale = 20
-- -- }, {
-- -- -- Features
-- -- module = "scale_bias",
-- -- source = {
-- -- module = "perlin",
-- -- frequency = 0.1,
-- -- octaves = 6,
-- -- },
-- -- scale = 8
-- -- }}
-- -- }
-- -- }, {
-- -- module = "scale_bias",
-- -- scale = 1,
-- -- bias = 1000,
-- -- source = {
-- -- module = "perlin",
-- -- frequency = 0.2
-- -- }
-- -- }}
-- -- }, {
-- -- module = "const",
-- -- value = -40
-- -- }}
-- -- }
-- volume = {
-- module = "scale",
-- y_scale = 2,
-- source = {
-- module = "add",
-- scalar = -2200,
-- source = {
-- module = "multiply",
-- scalar = 3000,
-- source = {
-- module = "add",
-- sources = {{
-- -- Elevation
-- module = "scale_bias",
-- source = {
-- module = "spheres",
-- frequency = 0.2
-- },
-- scale = 20
-- }, {
-- -- Features
-- module = "scale_bias",
-- source = {
-- module = "perlin",
-- frequency = 0.1,
-- octaves = 6,
-- },
-- scale = 8
-- }}
-- module = "simplex",
-- frequency = 0.0025,
-- octaves = 6,
-- lacunarity = 2
-- }
-- }, {
-- module = "scale_bias",
-- scale = 1,
-- bias = 1000,
-- source = {
-- module = "perlin",
-- frequency = 0.2
-- }
-- }}
-- }, {
-- module = "const",
-- value = -40
-- }}
-- }
-- }
-- }
volume = {
module = "scale",
y_scale = 2,
source = {
module = "add",
scalar = -2200,
source = {
module = "multiply",
scalar = 3000,
source = {
module = "simplex",
frequency = 0.0025,
octaves = 6,
lacunarity = 2
}
}
}
}
}
zepha.register_biome(identifier, {
environment = {
temperature = 40/100,
humidity = 20/100,
roughness = 10/100
},
blocks = {
top = "zeus:default:sand",
soil = "zeus:default:sand",
rock = "zeus:default:sandstone"
},
tags = { natural = 1, default = 1 },
biome_tint = "#e6fa61",
noise = noise,
structures = structures
})
return identifier;
-- }
--
-- zepha.register_biome(identifier, {
-- environment = {
-- temperature = 40/100,
-- humidity = 20/100,
-- roughness = 10/100
-- },
-- blocks = {
-- top = "zeus:default:sand",
-- soil = "zeus:default:sand",
-- rock = "zeus:default:sandstone"
-- },
-- tags = { natural = 1, default = 1 },
-- biome_tint = "#e6fa61",
-- noise = noise,
-- structures = structures
-- })
--
-- return identifier;

View File

@ -19,26 +19,6 @@ local structures = {}
-- layout = {{{ "zeus:flowers:flower_geranium" }}}
-- }))
for i = 1, 5 do
table.insert(structures, zepha.create_structure({
origin = V(),
probability = 0.1,
layout = {{{ "zeus:default:tall_grass_" .. tostring(i) }}}
}))
end
--
table.insert(structures, zepha.create_structure({
origin = V(),
probability = 0.025,
layout = {{{ "zeus:flowers:flower_geranium" }}}
}))
table.insert(structures, zepha.create_structure({
origin = V(),
probability = 0.025,
layout = {{{ "zeus:flowers:flower_white_dandelion" }}}
}))
table.insert(structures, zepha.create_structure({
origin = V(1),
probability = 0.025,
@ -141,26 +121,70 @@ table.insert(structures, zepha.create_structure({
}
}))
for i = 1, 5 do
table.insert(structures, zepha.create_structure({
origin = V(),
probability = 0.1,
layout = {{{ "zeus:default:tall_grass_" .. tostring(i) }}}
}))
end
--
table.insert(structures, zepha.create_structure({
origin = V(),
probability = 0.025,
layout = {{{ "zeus:flowers:flower_geranium" }}}
}))
table.insert(structures, zepha.create_structure({
origin = V(),
probability = 0.025,
layout = {{{ "zeus:flowers:flower_white_dandelion" }}}
}))
local noise = {
-- heightmap = runfile(_PATH .. 'world_noise'),
volume = {
module = "scale",
y_scale = 2,
source = {
module = "add",
scalar = -2200,
source = {
module = "multiply",
scalar = 3000,
heightmap = {
module = "add",
sources = {
runfile(_PATH .. 'world_noise'),
{
module = "max",
scalar = 0,
source = {
module = "simplex",
frequency = 0.0025,
octaves = 6,
lacunarity = 2
module = "add",
scalar = -150,
source = {
module = "multiply",
scalar = 400,
source = {
module = "simplex",
frequency = 0.00025,
lacunarity = 2.5,
octaves = 8,
persistence = 0.55
}
}
}
}
}
}
-- volume = {
-- module = "scale",
-- y_scale = 2,
-- source = {
-- module = "add",
-- scalar = -2200,
-- source = {
-- module = "multiply",
-- scalar = 3000,
-- source = {
-- module = "simplex",
-- frequency = 0.0025,
-- octaves = 6,
-- lacunarity = 2
-- }
-- }
-- }
-- }
}
zepha.register_biome(identifier, {

View File

@ -1,24 +1,11 @@
return {
module = "add",
sources = {{
-- Elevation
module = "scale_bias",
source = {
module = "perlin",
frequency = 0.002,
octaves = 8
},
scale = 250,
bias = -32
}, {
-- Features
module = "scale_bias",
source = {
module = "perlin",
frequency = 0.2,
octaves = 3,
},
scale = 6,
bias = 6
}}
module = "multiply",
scalar = 100,
source = {
module = "simplex",
frequency = 0.0002,
octaves = 5,
lacunarity = 3,
persistence = 0.45
}
};

View File

@ -5,8 +5,8 @@ zepha.create_dimension('zeus:world:default', {
biomes = { '#natural', '#default' }
})
zepha.create_dimension('zeus:world:endless_desert', {
biomes = { 'zeus:world:desert' }
})
-- zepha.create_dimension('zeus:world:endless_desert', {
-- biomes = { 'zeus:world:desert' }
-- })
zepha.set_default_dimension('zeus:world:default')