mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-09-09 03:59:53 -04:00
Implement block drop rendering and fix a couple synchronization issues.
This commit is contained in:
parent
44bb49ee5b
commit
3a6f55b8e7
@ -3,9 +3,11 @@
|
|||||||
in vec3 startPosition;
|
in vec3 startPosition;
|
||||||
in vec3 direction;
|
in vec3 direction;
|
||||||
in vec3 cameraSpacePos;
|
in vec3 cameraSpacePos;
|
||||||
flat in int faceNormal;
|
in vec2 uv;
|
||||||
|
flat in int faceNormalIndex;
|
||||||
|
flat in vec3 faceNormal;
|
||||||
flat in int voxelModel;
|
flat in int voxelModel;
|
||||||
flat in int blockType;
|
flat in int textureIndex;
|
||||||
flat in uvec3 lower;
|
flat in uvec3 lower;
|
||||||
flat in uvec3 upper;
|
flat in uvec3 upper;
|
||||||
|
|
||||||
@ -16,6 +18,13 @@ uniform mat4 projectionMatrix;
|
|||||||
uniform float sizeScale;
|
uniform float sizeScale;
|
||||||
uniform int time;
|
uniform int time;
|
||||||
|
|
||||||
|
uniform sampler2DArray texture_sampler;
|
||||||
|
uniform sampler2DArray emissionSampler;
|
||||||
|
uniform sampler2DArray reflectivityAndAbsorptionSampler;
|
||||||
|
uniform samplerCube reflectionMap;
|
||||||
|
uniform float reflectionMapSize;
|
||||||
|
uniform float contrast;
|
||||||
|
|
||||||
const float[6] normalVariations = float[6](
|
const float[6] normalVariations = float[6](
|
||||||
1.0,
|
1.0,
|
||||||
0.80,
|
0.80,
|
||||||
@ -25,191 +34,71 @@ const float[6] normalVariations = float[6](
|
|||||||
0.85
|
0.85
|
||||||
);
|
);
|
||||||
|
|
||||||
// blockDrops ------------------------------------------------------------------------------------------------------------------------
|
layout(std430, binding = 1) buffer _animatedTexture
|
||||||
|
{
|
||||||
|
float animatedTexture[];
|
||||||
|
};
|
||||||
|
|
||||||
const vec3[6] normals = vec3[6](
|
// block drops -------------------------------------------------------------------------------------------------------------------------
|
||||||
vec3(0, 0, 1),
|
|
||||||
vec3(0, 0, -1),
|
float lightVariation(vec3 normal) {
|
||||||
vec3(1, 0, 0),
|
const vec3 directionalPart = vec3(0, contrast/2, contrast);
|
||||||
vec3(-1, 0, 0),
|
const float baseLighting = 1 - contrast;
|
||||||
vec3(0, 1, 0),
|
return baseLighting + dot(normal, directionalPart);
|
||||||
vec3(0, -1, 0)
|
}
|
||||||
|
|
||||||
|
vec4 fixedCubeMapLookup(vec3 v) { // Taken from http://the-witness.net/news/2012/02/seamless-cube-map-filtering/
|
||||||
|
float M = max(max(abs(v.x), abs(v.y)), abs(v.z));
|
||||||
|
float scale = (reflectionMapSize - 1)/reflectionMapSize;
|
||||||
|
if (abs(v.x) != M) v.x *= scale;
|
||||||
|
if (abs(v.y) != M) v.y *= scale;
|
||||||
|
if (abs(v.z) != M) v.z *= scale;
|
||||||
|
return texture(reflectionMap, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
float ditherThresholds[16] = float[16] (
|
||||||
|
1/17.0, 9/17.0, 3/17.0, 11/17.0,
|
||||||
|
13/17.0, 5/17.0, 15/17.0, 7/17.0,
|
||||||
|
4/17.0, 12/17.0, 2/17.0, 10/17.0,
|
||||||
|
16/17.0, 8/17.0, 14/17.0, 6/17.0
|
||||||
);
|
);
|
||||||
|
|
||||||
#define modelSize 16
|
bool passDitherTest(float alpha) {
|
||||||
struct VoxelModel {
|
ivec2 screenPos = ivec2(gl_FragCoord.xy);
|
||||||
ivec4 minimum;
|
return alpha > ditherThresholds[screenPos.x*4 + screenPos.y];
|
||||||
ivec4 maximum;
|
|
||||||
uint bitPackedData[modelSize*modelSize*modelSize/8];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct TextureData {
|
|
||||||
uint textureIndices[6];
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(std430, binding = 1) buffer _textureData
|
|
||||||
{
|
|
||||||
TextureData textureData[];
|
|
||||||
};
|
|
||||||
layout(std430, binding = 4) buffer _blockVoxelModels
|
|
||||||
{
|
|
||||||
VoxelModel blockVoxelModels[];
|
|
||||||
};
|
|
||||||
uniform sampler2DArray texture_sampler;
|
|
||||||
uniform sampler2DArray emissionSampler;
|
|
||||||
|
|
||||||
int getVoxel(int voxelIndex) {
|
|
||||||
voxelIndex = (voxelIndex & 0xf) | (voxelIndex>>1 & 0xf0) | (voxelIndex>>2 & 0xf00);
|
|
||||||
int shift = 4*(voxelIndex & 7);
|
|
||||||
int arrayIndex = voxelIndex >> 3;
|
|
||||||
return (int(blockVoxelModels[voxelModel].bitPackedData[arrayIndex])>>shift & 15) - 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RayMarchResult {
|
|
||||||
bool hitAThing;
|
|
||||||
int normal;
|
|
||||||
int textureDir;
|
|
||||||
ivec3 voxelPosition;
|
|
||||||
};
|
|
||||||
|
|
||||||
RayMarchResult rayMarching(vec3 startPosition, vec3 direction) { // TODO: Mipmapped voxel models. (or maybe just remove them when they are far enough away?)
|
|
||||||
// Branchless implementation of "A Fast Voxel Traversal Algorithm for Ray Tracing" http://www.cse.yorku.ca/~amana/research/grid.pdf
|
|
||||||
vec3 step = sign(direction);
|
|
||||||
vec3 stepInIndex = step*vec3(1 << 10, 1 << 5, 1);
|
|
||||||
int overflowMask = 1<<14 | 1<<9 | 1<<4;
|
|
||||||
vec3 t1 = (floor(startPosition) - startPosition)/direction;
|
|
||||||
vec3 tDelta = 1/(direction);
|
|
||||||
vec3 t2 = t1 + tDelta;
|
|
||||||
tDelta = abs(tDelta);
|
|
||||||
vec3 tMax = max(t1, t2) - tDelta;
|
|
||||||
if(direction.x == 0) tMax.x = 1.0/0.0;
|
|
||||||
if(direction.y == 0) tMax.y = 1.0/0.0;
|
|
||||||
if(direction.z == 0) tMax.z = 1.0/0.0;
|
|
||||||
|
|
||||||
ivec3 voxelPos = ivec3(floor(startPosition));
|
|
||||||
int voxelIndex = voxelPos.x<<10 | voxelPos.y<<5 | voxelPos.z; // Stores the position as 0b0xxxx0yyyy0zzzz
|
|
||||||
|
|
||||||
int lastNormal = faceNormal;
|
|
||||||
int block = getVoxel(voxelIndex);
|
|
||||||
float total_tMax = 0;
|
|
||||||
|
|
||||||
int size = 16;
|
|
||||||
ivec3 sizeMask = ivec3(size - 1);
|
|
||||||
int it = 0;
|
|
||||||
while(block > 0 && it < 48) {
|
|
||||||
it++;
|
|
||||||
vec3 tNext = tMax + block*tDelta;
|
|
||||||
total_tMax = min(tNext.x, min(tNext.y, tNext.z));
|
|
||||||
vec3 missingSteps = floor((total_tMax - tMax)/tDelta + 0.00001);
|
|
||||||
voxelIndex += int(dot(missingSteps, stepInIndex));
|
|
||||||
tMax += missingSteps*tDelta;
|
|
||||||
if((voxelIndex & overflowMask) != 0)
|
|
||||||
return RayMarchResult(false, 0, 0, ivec3(0, 0, 0));
|
|
||||||
block = getVoxel(voxelIndex);
|
|
||||||
}
|
|
||||||
if(total_tMax != 0) {
|
|
||||||
if(tMax.x > tMax.y) {
|
|
||||||
if(tMax.x > tMax.z) {
|
|
||||||
lastNormal = 2 + (1 + int(step.x))/2;
|
|
||||||
} else {
|
|
||||||
lastNormal = 4 + (1 + int(step.z))/2;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(tMax.y > tMax.z) {
|
|
||||||
lastNormal = 0 + (1 + int(step.y))/2;
|
|
||||||
} else {
|
|
||||||
lastNormal = 4 + (1 + int(step.z))/2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
voxelPos.x = voxelIndex>>10 & 15;
|
|
||||||
voxelPos.y = voxelIndex>>5 & 15;
|
|
||||||
voxelPos.z = voxelIndex & 15;
|
|
||||||
int textureDir = -block;
|
|
||||||
if(textureDir == 6) textureDir = lastNormal;
|
|
||||||
vec3 modifiedCameraSpacePos = cameraSpacePos*(1 + total_tMax*sizeScale*length(direction)/length(cameraSpacePos));
|
|
||||||
vec4 projection = projectionMatrix*vec4(modifiedCameraSpacePos, 1);
|
|
||||||
float depth = projection.z/projection.w;
|
|
||||||
gl_FragDepth = ((gl_DepthRange.diff * depth) + gl_DepthRange.near + gl_DepthRange.far)/2.0;
|
|
||||||
return RayMarchResult(true, lastNormal, textureDir, voxelPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
ivec2 getTextureCoords(ivec3 voxelPosition, int textureDir) {
|
|
||||||
switch(textureDir) {
|
|
||||||
case 0:
|
|
||||||
return ivec2(voxelPosition.x, voxelPosition.y);
|
|
||||||
case 1:
|
|
||||||
return ivec2(15 - voxelPosition.x, voxelPosition.y);
|
|
||||||
case 2:
|
|
||||||
return ivec2(voxelPosition.y, voxelPosition.z);
|
|
||||||
case 3:
|
|
||||||
return ivec2(15 - voxelPosition.y, voxelPosition.z);
|
|
||||||
case 4:
|
|
||||||
return ivec2(15 - voxelPosition.x, voxelPosition.z);
|
|
||||||
case 5:
|
|
||||||
return ivec2(voxelPosition.x, voxelPosition.z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float getLod(ivec3 voxelPosition, int normal, vec3 direction, float variance) {
|
|
||||||
return max(0, min(4, log2(variance*length(direction)/abs(dot(vec3(normals[normal]), direction)))));
|
|
||||||
}
|
|
||||||
|
|
||||||
float perpendicularFwidth(vec3 direction) { // Estimates how big fwidth would be if the fragment normal was perpendicular to the light direction.
|
|
||||||
vec3 varianceX = dFdx(direction);
|
|
||||||
vec3 varianceY = dFdx(direction);
|
|
||||||
varianceX += direction;
|
|
||||||
varianceX = varianceX*length(direction)/length(varianceX);
|
|
||||||
varianceX -= direction;
|
|
||||||
varianceY += direction;
|
|
||||||
varianceY = varianceY*length(direction)/length(varianceY);
|
|
||||||
varianceY -= direction;
|
|
||||||
vec3 variance = abs(varianceX) + abs(varianceY);
|
|
||||||
return 8*length(variance);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 mipMapSample(sampler2DArray texture, ivec2 textureCoords, uint textureIndex, float lod) { // TODO: anisotropic filtering?
|
|
||||||
int lowerLod = int(floor(lod));
|
|
||||||
int higherLod = lowerLod+1;
|
|
||||||
float interpolation = lod - lowerLod;
|
|
||||||
vec4 lower = texelFetch(texture, ivec3(textureCoords >> lowerLod, textureIndex), lowerLod);
|
|
||||||
vec4 higher = texelFetch(texture, ivec3(textureCoords >> higherLod, textureIndex), higherLod);
|
|
||||||
return higher*interpolation + (1 - interpolation)*lower;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void mainBlockDrop() {
|
void mainBlockDrop() {
|
||||||
RayMarchResult result;
|
float animatedTextureIndex = animatedTexture[textureIndex];
|
||||||
float variance = perpendicularFwidth(direction);
|
float normalVariation = lightVariation(faceNormal);
|
||||||
if(variance <= 4.0) {
|
vec3 textureCoords = vec3(uv, animatedTextureIndex);
|
||||||
result = rayMarching(startPosition, direction);
|
|
||||||
} else {
|
|
||||||
result = RayMarchResult(true, faceNormal, faceNormal, ivec3(startPosition)); // At some point it doesn't make sense to even draw the model.
|
|
||||||
}
|
|
||||||
if(!result.hitAThing) discard;
|
|
||||||
uint textureIndex = textureData[blockType].textureIndices[result.textureDir];
|
|
||||||
float normalVariation = normalVariations[result.normal];
|
|
||||||
float lod = getLod(result.voxelPosition, result.normal, direction, variance);
|
|
||||||
ivec2 textureCoords = getTextureCoords(result.voxelPosition, result.textureDir);
|
|
||||||
fragColor = mipMapSample(texture_sampler, textureCoords, textureIndex, lod)*vec4(ambientLight*normalVariation, 1);
|
|
||||||
|
|
||||||
if (fragColor.a <= 0.1f) fragColor.a = 1; // TODO: Proper alpha handling.
|
float reflectivity = texture(reflectivityAndAbsorptionSampler, textureCoords).a;
|
||||||
|
float fresnelReflection = (1 + dot(normalize(direction), faceNormal));
|
||||||
|
fresnelReflection *= fresnelReflection;
|
||||||
|
fresnelReflection *= min(1, 2*reflectivity); // Limit it to 2*reflectivity to avoid making every block reflective.
|
||||||
|
reflectivity = reflectivity*fixedCubeMapLookup(reflect(direction, faceNormal)).x;
|
||||||
|
reflectivity = reflectivity*(1 - fresnelReflection) + fresnelReflection;
|
||||||
|
|
||||||
fragColor.rgb += mipMapSample(emissionSampler, textureCoords, textureIndex, lod).rgb;
|
vec3 pixelLight = max(vec3(normalVariation), texture(emissionSampler, textureCoords).r*4); // TODO: light*normalVariation
|
||||||
|
fragColor = texture(texture_sampler, textureCoords)*vec4(pixelLight, 1);
|
||||||
|
fragColor.rgb += reflectivity*pixelLight;
|
||||||
|
|
||||||
fragColor.rgb /= 4;
|
if(!passDitherTest(fragColor.a)) discard;
|
||||||
|
fragColor.a = 1;
|
||||||
|
gl_FragDepth = gl_FragCoord.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
// itemDrops -------------------------------------------------------------------------------------------------------------------------
|
// itemDrops -------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
layout(std430, binding = 2) buffer _itemVoxelModels
|
layout(std430, binding = 2) buffer _modelInfo
|
||||||
{
|
{
|
||||||
uint itemVoxelModels[];
|
uint modelInfo[];
|
||||||
};
|
};
|
||||||
|
|
||||||
uint getVoxel(uvec3 pos) {
|
uint getVoxel(uvec3 pos) {
|
||||||
uint index = (pos.x | pos.y*upper.x)*upper.z | pos.z;
|
uint index = (pos.x | pos.y*upper.x)*upper.z | pos.z;
|
||||||
return itemVoxelModels[voxelModel + index];
|
return modelInfo[voxelModel + index];
|
||||||
}
|
}
|
||||||
|
|
||||||
vec4 decodeColor(uint block) {
|
vec4 decodeColor(uint block) {
|
||||||
@ -229,7 +118,7 @@ void mainItemDrop() {
|
|||||||
if(direction.z == 0) tMax.z = 1.0/0.0;
|
if(direction.z == 0) tMax.z = 1.0/0.0;
|
||||||
|
|
||||||
uvec3 voxelPosition = uvec3(floor(startPosition));
|
uvec3 voxelPosition = uvec3(floor(startPosition));
|
||||||
int lastNormal = faceNormal;
|
int lastNormal = faceNormalIndex;
|
||||||
uint block = getVoxel(voxelPosition);
|
uint block = getVoxel(voxelPosition);
|
||||||
float total_tMax = 0;
|
float total_tMax = 0;
|
||||||
|
|
||||||
@ -286,7 +175,7 @@ void mainItemDrop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
if(blockType != 0) {
|
if(textureIndex >= 0) {
|
||||||
mainBlockDrop();
|
mainBlockDrop();
|
||||||
} else {
|
} else {
|
||||||
mainItemDrop();
|
mainItemDrop();
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
#version 430
|
#version 430
|
||||||
|
|
||||||
layout (location=0) in int positionAndNormals;
|
|
||||||
|
|
||||||
out vec3 startPosition;
|
out vec3 startPosition;
|
||||||
out vec3 direction;
|
out vec3 direction;
|
||||||
out vec3 cameraSpacePos;
|
out vec3 cameraSpacePos;
|
||||||
flat out int faceNormal;
|
out vec2 uv;
|
||||||
|
flat out int faceNormalIndex;
|
||||||
|
flat out vec3 faceNormal;
|
||||||
flat out int voxelModel;
|
flat out int voxelModel;
|
||||||
flat out int blockType;
|
flat out int textureIndex;
|
||||||
flat out uvec3 lower;
|
flat out uvec3 lower;
|
||||||
flat out uvec3 upper;
|
flat out uvec3 upper;
|
||||||
|
|
||||||
@ -18,47 +18,95 @@ uniform int modelIndex;
|
|||||||
uniform int block;
|
uniform int block;
|
||||||
uniform float sizeScale;
|
uniform float sizeScale;
|
||||||
|
|
||||||
layout(std430, binding = 2) buffer _itemVoxelModels
|
layout(std430, binding = 2) buffer _modelInfo
|
||||||
{
|
{
|
||||||
uint itemVoxelModels[];
|
uint modelInfo[];
|
||||||
};
|
};
|
||||||
|
|
||||||
#define modelSize 16
|
struct QuadInfo {
|
||||||
struct VoxelModel {
|
vec3 normal;
|
||||||
ivec4 minimum;
|
vec3 corners[4];
|
||||||
ivec4 maximum;
|
vec2 cornerUV[4];
|
||||||
uint bitPackedData[modelSize*modelSize*modelSize/8];
|
uint textureSlot;
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(std430, binding = 4) buffer _blockVoxelModels
|
layout(std430, binding = 4) buffer _quads
|
||||||
{
|
{
|
||||||
VoxelModel blockVoxelModels[];
|
QuadInfo quads[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const int[24] positions = int[24](
|
||||||
|
0x010,
|
||||||
|
0x110,
|
||||||
|
0x011,
|
||||||
|
0x111,
|
||||||
|
|
||||||
|
0x000,
|
||||||
|
0x001,
|
||||||
|
0x100,
|
||||||
|
0x101,
|
||||||
|
|
||||||
|
0x100,
|
||||||
|
0x101,
|
||||||
|
0x110,
|
||||||
|
0x111,
|
||||||
|
|
||||||
|
0x000,
|
||||||
|
0x010,
|
||||||
|
0x001,
|
||||||
|
0x011,
|
||||||
|
|
||||||
|
0x001,
|
||||||
|
0x011,
|
||||||
|
0x101,
|
||||||
|
0x111,
|
||||||
|
|
||||||
|
0x000,
|
||||||
|
0x100,
|
||||||
|
0x010,
|
||||||
|
0x110
|
||||||
|
);
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
ivec3 pos = ivec3 (
|
int faceID = gl_VertexID >> 2;
|
||||||
positionAndNormals >> 2 & 1,
|
int vertexID = gl_VertexID & 3;
|
||||||
positionAndNormals >> 1 & 1,
|
|
||||||
positionAndNormals >> 0 & 1
|
|
||||||
);
|
|
||||||
faceNormal = positionAndNormals >> 3;
|
|
||||||
int voxelModelIndex = modelIndex;
|
int voxelModelIndex = modelIndex;
|
||||||
bool isBlock = block != 0;
|
bool isBlock = block != 0;
|
||||||
|
vec3 pos;
|
||||||
if(isBlock) {
|
if(isBlock) {
|
||||||
lower = uvec3(blockVoxelModels[voxelModelIndex].minimum.xyz);
|
uint modelAndTexture = modelInfo[voxelModelIndex + faceID*2];
|
||||||
upper = uvec3(blockVoxelModels[voxelModelIndex].maximum.xyz);
|
uint offsetByNormal = modelInfo[voxelModelIndex + faceID*2 + 1];
|
||||||
|
uint quadIndex = modelAndTexture >> 16u;
|
||||||
|
textureIndex = int(modelAndTexture & 65535u);
|
||||||
|
|
||||||
|
pos = quads[quadIndex].corners[vertexID];
|
||||||
|
uv = quads[quadIndex].cornerUV[vertexID];
|
||||||
|
if(offsetByNormal != 0) {
|
||||||
|
pos += quads[quadIndex].normal;
|
||||||
|
}
|
||||||
|
faceNormal = quads[quadIndex].normal;
|
||||||
} else {
|
} else {
|
||||||
upper.x = itemVoxelModels[voxelModelIndex++];
|
int position = positions[gl_VertexID];
|
||||||
upper.y = itemVoxelModels[voxelModelIndex++];
|
pos = vec3 (
|
||||||
upper.z = itemVoxelModels[voxelModelIndex++];
|
position >> 8 & 1,
|
||||||
|
position >> 4 & 1,
|
||||||
|
position >> 0 & 1
|
||||||
|
);
|
||||||
|
faceNormalIndex = faceID;
|
||||||
|
upper.x = modelInfo[voxelModelIndex++];
|
||||||
|
upper.y = modelInfo[voxelModelIndex++];
|
||||||
|
upper.z = modelInfo[voxelModelIndex++];
|
||||||
lower = uvec3(0);
|
lower = uvec3(0);
|
||||||
|
|
||||||
|
startPosition = lower + vec3(upper - lower)*0.999*pos;
|
||||||
|
pos = pos*(upper - lower)*sizeScale + sizeScale/2;
|
||||||
|
textureIndex = -1;
|
||||||
}
|
}
|
||||||
voxelModel = voxelModelIndex;
|
voxelModel = voxelModelIndex;
|
||||||
blockType = block;
|
|
||||||
|
|
||||||
startPosition = lower + vec3(upper - lower)*0.999*pos;
|
|
||||||
|
|
||||||
vec4 worldSpace = modelMatrix*vec4(pos*(upper - lower)*sizeScale + sizeScale/2, 1);
|
vec4 worldSpace = modelMatrix*vec4(pos, 1);
|
||||||
direction = (transpose(mat3(modelMatrix))*worldSpace.xyz).xyz;
|
direction = (transpose(mat3(modelMatrix))*worldSpace.xyz).xyz;
|
||||||
|
|
||||||
vec4 cameraSpace = viewMatrix*worldSpace;
|
vec4 cameraSpace = viewMatrix*worldSpace;
|
||||||
|
204
src/itemdrop.zig
204
src/itemdrop.zig
@ -134,6 +134,16 @@ pub const ItemDropManager = struct { // MARK: ItemDropManager
|
|||||||
return _data;
|
return _data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getInitialList(self: *ItemDropManager, allocator: NeverFailingAllocator) JsonElement {
|
||||||
|
var list = JsonElement.initArray(allocator);
|
||||||
|
var ii: u32 = 0;
|
||||||
|
while(ii < self.size) : (ii += 1) {
|
||||||
|
const i = self.indices[ii];
|
||||||
|
list.JsonArray.append(self.storeSingle(self.lastUpdates.JsonArray.allocator, i));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
fn storeSingle(self: *ItemDropManager, allocator: NeverFailingAllocator, i: u16) JsonElement {
|
fn storeSingle(self: *ItemDropManager, allocator: NeverFailingAllocator, i: u16) JsonElement {
|
||||||
main.utils.assertLocked(&self.mutex);
|
main.utils.assertLocked(&self.mutex);
|
||||||
const obj = JsonElement.initObject(allocator);
|
const obj = JsonElement.initObject(allocator);
|
||||||
@ -182,7 +192,7 @@ pub const ItemDropManager = struct { // MARK: ItemDropManager
|
|||||||
pickupCooldown[i] -= 1;
|
pickupCooldown[i] -= 1;
|
||||||
despawnTime[i] -= 1;
|
despawnTime[i] -= 1;
|
||||||
if(despawnTime[i] < 0) {
|
if(despawnTime[i] < 0) {
|
||||||
self.remove(i);
|
self.removeLocked(i);
|
||||||
} else {
|
} else {
|
||||||
ii += 1;
|
ii += 1;
|
||||||
}
|
}
|
||||||
@ -272,9 +282,8 @@ pub const ItemDropManager = struct { // MARK: ItemDropManager
|
|||||||
self.size += 1;
|
self.size += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(self: *ItemDropManager, i: u16) void {
|
fn removeLocked(self: *ItemDropManager, i: u16) void {
|
||||||
self.mutex.lock();
|
main.utils.assertLocked(&self.mutex);
|
||||||
defer self.mutex.unlock();
|
|
||||||
self.size -= 1;
|
self.size -= 1;
|
||||||
const ii = self.list.items(.reverseIndex)[i];
|
const ii = self.list.items(.reverseIndex)[i];
|
||||||
self.indices[ii] = self.indices[self.size];
|
self.indices[ii] = self.indices[self.size];
|
||||||
@ -285,6 +294,12 @@ pub const ItemDropManager = struct { // MARK: ItemDropManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn remove(self: *ItemDropManager, i: u16) void {
|
||||||
|
self.mutex.lock();
|
||||||
|
defer self.mutex.unlock();
|
||||||
|
self.removeLocked(i);
|
||||||
|
}
|
||||||
|
|
||||||
fn updateEnt(self: *ItemDropManager, chunk: *ServerChunk, pos: *Vec3d, vel: *Vec3d, deltaTime: f64) void {
|
fn updateEnt(self: *ItemDropManager, chunk: *ServerChunk, pos: *Vec3d, vel: *Vec3d, deltaTime: f64) void {
|
||||||
main.utils.assertLocked(&self.mutex);
|
main.utils.assertLocked(&self.mutex);
|
||||||
const startedInABlock = self.checkBlocks(chunk, pos);
|
const startedInABlock = self.checkBlocks(chunk, pos);
|
||||||
@ -506,58 +521,84 @@ pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer
|
|||||||
time: c_int,
|
time: c_int,
|
||||||
texture_sampler: c_int,
|
texture_sampler: c_int,
|
||||||
emissionSampler: c_int,
|
emissionSampler: c_int,
|
||||||
|
reflectivityAndAbsorptionSampler: c_int,
|
||||||
|
reflectionMap: c_int,
|
||||||
|
reflectionMapSize: c_int,
|
||||||
|
contrast: c_int,
|
||||||
} = undefined;
|
} = undefined;
|
||||||
|
|
||||||
var itemModelSSBO: graphics.SSBO = undefined;
|
var itemModelSSBO: graphics.SSBO = undefined;
|
||||||
var itemVAO: c_uint = undefined;
|
|
||||||
var itemVBOs: [2]c_uint = undefined;
|
|
||||||
|
|
||||||
var modelData: main.List(u32) = undefined;
|
var modelData: main.List(u32) = undefined;
|
||||||
var freeSlots: main.List(*ItemVoxelModel) = undefined;
|
var freeSlots: main.List(*ItemVoxelModel) = undefined;
|
||||||
|
|
||||||
const ItemVoxelModel = struct {
|
const ItemVoxelModel = struct {
|
||||||
index: u31 = undefined,
|
index: u31 = undefined,
|
||||||
size: Vec3i = undefined,
|
len: u31 = undefined,
|
||||||
item: items.Item,
|
item: items.Item,
|
||||||
|
|
||||||
|
fn getSlot(len: u31) u31 {
|
||||||
|
for(freeSlots.items, 0..) |potentialSlot, i| {
|
||||||
|
if(std.meta.eql(len, potentialSlot.len)) {
|
||||||
|
_ = freeSlots.swapRemove(i);
|
||||||
|
const result = potentialSlot.index;
|
||||||
|
main.globalAllocator.destroy(potentialSlot);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const result: u31 = @intCast(modelData.items.len);
|
||||||
|
modelData.resize(result + len);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
fn init(template: ItemVoxelModel) *ItemVoxelModel {
|
fn init(template: ItemVoxelModel) *ItemVoxelModel {
|
||||||
const self = main.globalAllocator.create(ItemVoxelModel);
|
const self = main.globalAllocator.create(ItemVoxelModel);
|
||||||
self.* = ItemVoxelModel{
|
self.* = ItemVoxelModel{
|
||||||
.item = template.item,
|
.item = template.item,
|
||||||
};
|
};
|
||||||
// Find sizes and free index:
|
if(self.item == .baseItem and self.item.baseItem.block != null and self.item.baseItem.image.imageData.ptr == graphics.Image.defaultImage.imageData.ptr) {
|
||||||
const img = self.item.getImage();
|
// Find sizes and free index:
|
||||||
self.size = Vec3i{img.width, 1, img.height};
|
const block = blocks.Block{.typ = self.item.baseItem.block.?, .data = 0}; // TODO: Natural standard
|
||||||
var freeSlot: ?*ItemVoxelModel = null;
|
const modelIndex = blocks.meshes.model(block);
|
||||||
for(freeSlots.items, 0..) |potentialSlot, i| {
|
const model = &main.models.models.items[modelIndex];
|
||||||
if(std.meta.eql(self.size, potentialSlot.size)) {
|
var data = main.List(u32).init(main.stackAllocator);
|
||||||
freeSlot = potentialSlot;
|
defer data.deinit();
|
||||||
_ = freeSlots.swapRemove(i);
|
for(model.internalQuads) |quad| {
|
||||||
break;
|
const textureIndex = blocks.meshes.textureIndex(block, main.models.quads.items[quad].textureSlot);
|
||||||
|
data.append(@as(u32, quad) << 16 | textureIndex); // modelAndTexture
|
||||||
|
data.append(0); // offsetByNormal
|
||||||
}
|
}
|
||||||
}
|
for(model.neighborFacingQuads) |list| {
|
||||||
const modelDataSize: u32 = @intCast(3 + @reduce(.Mul, self.size));
|
for(list) |quad| {
|
||||||
var dataSection: []u32 = undefined;
|
const textureIndex = blocks.meshes.textureIndex(block, main.models.quads.items[quad].textureSlot);
|
||||||
if(freeSlot) |_freeSlot| {
|
data.append(@as(u32, quad) << 16 | textureIndex); // modelAndTexture
|
||||||
main.globalAllocator.destroy(_freeSlot);
|
data.append(1); // offsetByNormal
|
||||||
self.index = _freeSlot.index;
|
}
|
||||||
|
}
|
||||||
|
self.len = @intCast(data.items.len);
|
||||||
|
self.index = getSlot(self.len);
|
||||||
|
@memcpy(modelData.items[self.index..][0..self.len], data.items);
|
||||||
} else {
|
} else {
|
||||||
self.index = @intCast(modelData.items.len);
|
// Find sizes and free index:
|
||||||
modelData.resize(self.index + modelDataSize);
|
const img = self.item.getImage();
|
||||||
}
|
const size = Vec3i{img.width, 1, img.height};
|
||||||
dataSection = modelData.items[self.index..][0..modelDataSize];
|
self.len = @intCast(3 + @reduce(.Mul, size));
|
||||||
dataSection[0] = @intCast(self.size[0]);
|
self.index = getSlot(self.len);
|
||||||
dataSection[1] = @intCast(self.size[1]);
|
var dataSection: []u32 = undefined;
|
||||||
dataSection[2] = @intCast(self.size[2]);
|
dataSection = modelData.items[self.index..][0..self.len];
|
||||||
var i: u32 = 3;
|
dataSection[0] = @intCast(size[0]);
|
||||||
var z: u32 = 0;
|
dataSection[1] = @intCast(size[1]);
|
||||||
while(z < 1) : (z += 1) {
|
dataSection[2] = @intCast(size[2]);
|
||||||
var x: u32 = 0;
|
var i: u32 = 3;
|
||||||
while(x < img.width) : (x += 1) {
|
var z: u32 = 0;
|
||||||
var y: u32 = 0;
|
while(z < 1) : (z += 1) {
|
||||||
while(y < img.height) : (y += 1) {
|
var x: u32 = 0;
|
||||||
dataSection[i] = img.getRGB(x, y).toARBG();
|
while(x < img.width) : (x += 1) {
|
||||||
i += 1;
|
var y: u32 = 0;
|
||||||
|
while(y < img.height) : (y += 1) {
|
||||||
|
dataSection[i] = img.getRGB(x, y).toARBG();
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -585,70 +626,6 @@ pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer
|
|||||||
itemModelSSBO.bufferData(i32, &[3]i32{1, 1, 1});
|
itemModelSSBO.bufferData(i32, &[3]i32{1, 1, 1});
|
||||||
itemModelSSBO.bind(2);
|
itemModelSSBO.bind(2);
|
||||||
|
|
||||||
const positions = [_]i32 {
|
|
||||||
0b011000,
|
|
||||||
0b011001,
|
|
||||||
0b011010,
|
|
||||||
0b011011,
|
|
||||||
|
|
||||||
0b001000,
|
|
||||||
0b001001,
|
|
||||||
0b001100,
|
|
||||||
0b001101,
|
|
||||||
|
|
||||||
0b101000,
|
|
||||||
0b101010,
|
|
||||||
0b101100,
|
|
||||||
0b101110,
|
|
||||||
|
|
||||||
0b010100,
|
|
||||||
0b010101,
|
|
||||||
0b010110,
|
|
||||||
0b010111,
|
|
||||||
|
|
||||||
0b000010,
|
|
||||||
0b000011,
|
|
||||||
0b000110,
|
|
||||||
0b000111,
|
|
||||||
|
|
||||||
0b100001,
|
|
||||||
0b100011,
|
|
||||||
0b100101,
|
|
||||||
0b100111,
|
|
||||||
};
|
|
||||||
const indices = [_]i32 {
|
|
||||||
0, 1, 3,
|
|
||||||
0, 3, 2,
|
|
||||||
|
|
||||||
4, 7, 5,
|
|
||||||
4, 6, 7,
|
|
||||||
|
|
||||||
8, 9, 11,
|
|
||||||
8, 11, 10,
|
|
||||||
|
|
||||||
12, 15, 13,
|
|
||||||
12, 14, 15,
|
|
||||||
|
|
||||||
16, 17, 19,
|
|
||||||
16, 19, 18,
|
|
||||||
|
|
||||||
20, 23, 21,
|
|
||||||
20, 22, 23,
|
|
||||||
};
|
|
||||||
c.glGenVertexArrays(1, &itemVAO);
|
|
||||||
c.glBindVertexArray(itemVAO);
|
|
||||||
c.glEnableVertexAttribArray(0);
|
|
||||||
|
|
||||||
c.glGenBuffers(2, &itemVBOs);
|
|
||||||
c.glBindBuffer(c.GL_ARRAY_BUFFER, itemVBOs[0]);
|
|
||||||
c.glBufferData(c.GL_ARRAY_BUFFER, @intCast(positions.len*@sizeOf(i32)), &positions, c.GL_STATIC_DRAW);
|
|
||||||
c.glVertexAttribPointer(0, 1, c.GL_FLOAT, c.GL_FALSE, @sizeOf(i32), null);
|
|
||||||
|
|
||||||
c.glBindBuffer(c.GL_ELEMENT_ARRAY_BUFFER, itemVBOs[1]);
|
|
||||||
c.glBufferData(c.GL_ELEMENT_ARRAY_BUFFER, @intCast(indices.len*@sizeOf(i32)), &indices, c.GL_STATIC_DRAW);
|
|
||||||
|
|
||||||
c.glBindVertexArray(0);
|
|
||||||
|
|
||||||
modelData = main.List(u32).init(main.globalAllocator);
|
modelData = main.List(u32).init(main.globalAllocator);
|
||||||
freeSlots = main.List(*ItemVoxelModel).init(main.globalAllocator);
|
freeSlots = main.List(*ItemVoxelModel).init(main.globalAllocator);
|
||||||
}
|
}
|
||||||
@ -656,8 +633,6 @@ pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer
|
|||||||
pub fn deinit() void {
|
pub fn deinit() void {
|
||||||
itemShader.deinit();
|
itemShader.deinit();
|
||||||
itemModelSSBO.deinit();
|
itemModelSSBO.deinit();
|
||||||
c.glDeleteVertexArrays(1, &itemVAO);
|
|
||||||
c.glDeleteBuffers(2, &itemVBOs);
|
|
||||||
modelData.deinit();
|
modelData.deinit();
|
||||||
voxelModels.clear();
|
voxelModels.clear();
|
||||||
for(freeSlots.items) |freeSlot| {
|
for(freeSlots.items) |freeSlot| {
|
||||||
@ -668,9 +643,9 @@ pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer
|
|||||||
|
|
||||||
var voxelModels: utils.Cache(ItemVoxelModel, 32, 32, ItemVoxelModel.deinit) = .{};
|
var voxelModels: utils.Cache(ItemVoxelModel, 32, 32, ItemVoxelModel.deinit) = .{};
|
||||||
|
|
||||||
fn getModelIndex(item: items.Item) u31 {
|
fn getModel(item: items.Item) *ItemVoxelModel {
|
||||||
const compareObject = ItemVoxelModel{.item = item};
|
const compareObject = ItemVoxelModel{.item = item};
|
||||||
return voxelModels.findOrCreate(compareObject, ItemVoxelModel.init, null).index;
|
return voxelModels.findOrCreate(compareObject, ItemVoxelModel.init, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn renderItemDrops(projMatrix: Mat4f, ambientLight: Vec3f, playerPos: Vec3d, time: u32) void {
|
pub fn renderItemDrops(projMatrix: Mat4f, ambientLight: Vec3f, playerPos: Vec3d, time: u32) void {
|
||||||
@ -678,11 +653,15 @@ pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer
|
|||||||
itemShader.bind();
|
itemShader.bind();
|
||||||
c.glUniform1i(itemUniforms.texture_sampler, 0);
|
c.glUniform1i(itemUniforms.texture_sampler, 0);
|
||||||
c.glUniform1i(itemUniforms.emissionSampler, 1);
|
c.glUniform1i(itemUniforms.emissionSampler, 1);
|
||||||
|
c.glUniform1i(itemUniforms.reflectivityAndAbsorptionSampler, 2);
|
||||||
|
c.glUniform1i(itemUniforms.reflectionMap, 4);
|
||||||
|
c.glUniform1f(itemUniforms.reflectionMapSize, main.renderer.reflectionCubeMapSize);
|
||||||
c.glUniform1i(itemUniforms.time, @as(u31, @truncate(time)));
|
c.glUniform1i(itemUniforms.time, @as(u31, @truncate(time)));
|
||||||
c.glUniformMatrix4fv(itemUniforms.projectionMatrix, 1, c.GL_TRUE, @ptrCast(&projMatrix));
|
c.glUniformMatrix4fv(itemUniforms.projectionMatrix, 1, c.GL_TRUE, @ptrCast(&projMatrix));
|
||||||
c.glUniform3fv(itemUniforms.ambientLight, 1, @ptrCast(&ambientLight));
|
c.glUniform3fv(itemUniforms.ambientLight, 1, @ptrCast(&ambientLight));
|
||||||
c.glUniformMatrix4fv(itemUniforms.viewMatrix, 1, c.GL_TRUE, @ptrCast(&game.camera.viewMatrix));
|
c.glUniformMatrix4fv(itemUniforms.viewMatrix, 1, c.GL_TRUE, @ptrCast(&game.camera.viewMatrix));
|
||||||
c.glUniform1f(itemUniforms.sizeScale, @floatCast(ItemDropManager.diameter/4.0));
|
c.glUniform1f(itemUniforms.sizeScale, @floatCast(ItemDropManager.diameter/4.0));
|
||||||
|
c.glUniform1f(itemUniforms.contrast, 0.12);
|
||||||
const itemDrops = &game.world.?.itemDrops.super;
|
const itemDrops = &game.world.?.itemDrops.super;
|
||||||
itemDrops.mutex.lock();
|
itemDrops.mutex.lock();
|
||||||
defer itemDrops.mutex.unlock();
|
defer itemDrops.mutex.unlock();
|
||||||
@ -702,18 +681,19 @@ pub const ItemDropRenderer = struct { // MARK: ItemDropRenderer
|
|||||||
modelMatrix = modelMatrix.mul(Mat4f.rotationZ(-rot[2]));
|
modelMatrix = modelMatrix.mul(Mat4f.rotationZ(-rot[2]));
|
||||||
c.glUniformMatrix4fv(itemUniforms.modelMatrix, 1, c.GL_TRUE, @ptrCast(&modelMatrix));
|
c.glUniformMatrix4fv(itemUniforms.modelMatrix, 1, c.GL_TRUE, @ptrCast(&modelMatrix));
|
||||||
|
|
||||||
|
const model = getModel(item);
|
||||||
|
c.glUniform1i(itemUniforms.modelIndex, model.index);
|
||||||
|
var vertices: u31 = 36;
|
||||||
|
|
||||||
if(item == .baseItem and item.baseItem.block != null and item.baseItem.image.imageData.ptr == graphics.Image.defaultImage.imageData.ptr) {
|
if(item == .baseItem and item.baseItem.block != null and item.baseItem.image.imageData.ptr == graphics.Image.defaultImage.imageData.ptr) {
|
||||||
const blockType = item.baseItem.block.?;
|
const blockType = item.baseItem.block.?;
|
||||||
const block = blocks.Block{.typ = blockType, .data = 0};
|
|
||||||
c.glUniform1i(itemUniforms.modelIndex, block.mode().model(block));
|
|
||||||
c.glUniform1i(itemUniforms.block, blockType);
|
c.glUniform1i(itemUniforms.block, blockType);
|
||||||
|
vertices = model.len/2*6;
|
||||||
} else {
|
} else {
|
||||||
const index = getModelIndex(item);
|
|
||||||
c.glUniform1i(itemUniforms.modelIndex, index);
|
|
||||||
c.glUniform1i(itemUniforms.block, 0);
|
c.glUniform1i(itemUniforms.block, 0);
|
||||||
}
|
}
|
||||||
c.glBindVertexArray(itemVAO);
|
c.glBindVertexArray(main.renderer.chunk_meshing.vao);
|
||||||
c.glDrawElements(c.GL_TRIANGLES, 36, c.GL_UNSIGNED_INT, null);
|
c.glDrawElements(c.GL_TRIANGLES, vertices, c.GL_UNSIGNED_INT, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ pub var occlusionTestUniforms: struct {
|
|||||||
playerPositionInteger: c_int,
|
playerPositionInteger: c_int,
|
||||||
playerPositionFraction: c_int,
|
playerPositionFraction: c_int,
|
||||||
} = undefined;
|
} = undefined;
|
||||||
var vao: c_uint = undefined;
|
pub var vao: c_uint = undefined;
|
||||||
var vbo: c_uint = undefined;
|
var vbo: c_uint = undefined;
|
||||||
pub var faceBuffer: graphics.LargeBuffer(FaceData) = undefined;
|
pub var faceBuffer: graphics.LargeBuffer(FaceData) = undefined;
|
||||||
pub var lightBuffer: graphics.LargeBuffer(u32) = undefined;
|
pub var lightBuffer: graphics.LargeBuffer(u32) = undefined;
|
||||||
|
@ -252,14 +252,7 @@ fn deinit() void {
|
|||||||
command.deinit();
|
command.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update() void { // MARK: update()
|
fn sendEntityUpdates(comptime getInitialList: bool, allocator: utils.NeverFailingAllocator) if(getInitialList) []const u8 else void {
|
||||||
world.?.update();
|
|
||||||
mutex.lock();
|
|
||||||
for(users.items) |user| {
|
|
||||||
user.update();
|
|
||||||
}
|
|
||||||
mutex.unlock();
|
|
||||||
|
|
||||||
// Send the entity updates:
|
// Send the entity updates:
|
||||||
const updateList = main.JsonElement.initArray(main.stackAllocator);
|
const updateList = main.JsonElement.initArray(main.stackAllocator);
|
||||||
defer updateList.free(main.stackAllocator);
|
defer updateList.free(main.stackAllocator);
|
||||||
@ -276,13 +269,37 @@ fn update() void { // MARK: update()
|
|||||||
world.?.itemDropManager.lastUpdates.free(alloc);
|
world.?.itemDropManager.lastUpdates.free(alloc);
|
||||||
world.?.itemDropManager.lastUpdates = main.JsonElement.initArray(alloc);
|
world.?.itemDropManager.lastUpdates = main.JsonElement.initArray(alloc);
|
||||||
}
|
}
|
||||||
|
var initialList: []const u8 = undefined;
|
||||||
|
if(getInitialList) {
|
||||||
|
const list = main.JsonElement.initArray(main.stackAllocator);
|
||||||
|
defer list.free(main.stackAllocator);
|
||||||
|
list.JsonArray.append(.{.JsonNull = {}});
|
||||||
|
const itemDropList = world.?.itemDropManager.getInitialList(main.stackAllocator);
|
||||||
|
list.JsonArray.appendSlice(itemDropList.JsonArray.items);
|
||||||
|
itemDropList.JsonArray.items.len = 0;
|
||||||
|
itemDropList.free(main.stackAllocator);
|
||||||
|
initialList = list.toStringEfficient(allocator, &.{});
|
||||||
|
}
|
||||||
world.?.itemDropManager.mutex.unlock();
|
world.?.itemDropManager.mutex.unlock();
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
for(users.items) |user| {
|
for(users.items) |user| {
|
||||||
main.network.Protocols.entity.send(user.conn, updateData);
|
main.network.Protocols.entity.send(user.conn, updateData);
|
||||||
}
|
}
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
|
if(getInitialList) {
|
||||||
|
return initialList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update() void { // MARK: update()
|
||||||
|
world.?.update();
|
||||||
|
mutex.lock();
|
||||||
|
for(users.items) |user| {
|
||||||
|
user.update();
|
||||||
|
}
|
||||||
|
mutex.unlock();
|
||||||
|
|
||||||
|
sendEntityUpdates(false, main.stackAllocator);
|
||||||
|
|
||||||
|
|
||||||
// Send the entity data:
|
// Send the entity data:
|
||||||
@ -415,6 +432,9 @@ pub fn connect(user: *User) void {
|
|||||||
if(user.connected.load(.unordered)) main.network.Protocols.entity.send(user.conn, data);
|
if(user.connected.load(.unordered)) main.network.Protocols.entity.send(user.conn, data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
const initialList = sendEntityUpdates(true, main.stackAllocator);
|
||||||
|
main.network.Protocols.entity.send(user.conn, initialList);
|
||||||
|
main.stackAllocator.free(initialList);
|
||||||
const message = std.fmt.allocPrint(main.stackAllocator.allocator, "{s} #ffff00joined", .{user.name}) catch unreachable;
|
const message = std.fmt.allocPrint(main.stackAllocator.allocator, "{s} #ffff00joined", .{user.name}) catch unreachable;
|
||||||
defer main.stackAllocator.free(message);
|
defer main.stackAllocator.free(message);
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user