update
This commit is contained in:
@@ -2,22 +2,41 @@
|
|||||||
|
|
||||||
#define PI32 3.141592653589f
|
#define PI32 3.141592653589f
|
||||||
|
|
||||||
|
internal void renderPlayer(GameOffscreenBuffer *buffer, int playerX, int playerY) {
|
||||||
|
uint8 *endOfBuffer = (uint8 *)buffer->memory + buffer->pitch*buffer->height;
|
||||||
|
uint32 color = 0xFFFFFFFF;
|
||||||
|
int top = playerY;
|
||||||
|
int bottom = playerY + 10;
|
||||||
|
for (int x = playerX; x < playerX + 10; x++) {
|
||||||
|
uint8 *pixel = ((uint8 *)buffer->memory + x * buffer->bytesPerPixel + top*buffer->pitch);
|
||||||
|
for (int y = top; y < bottom; y++) {
|
||||||
|
if ((pixel >= buffer->memory) && (pixel + 4 < endOfBuffer)) {
|
||||||
|
*(uint32 *)pixel = color;
|
||||||
|
}
|
||||||
|
pixel += buffer->pitch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
internal void outputSineSound(GameSoundOutputBuffer *soundBuffer, GameState *state) {
|
internal void outputSineSound(GameSoundOutputBuffer *soundBuffer, GameState *state) {
|
||||||
int16 toneVolume = 3000;
|
int16 toneVolume = 3000;
|
||||||
int wavePeriod = soundBuffer->samplesPerSecond/state->toneHz;
|
int wavePeriod = soundBuffer->samplesPerSecond/state->toneHz;
|
||||||
|
|
||||||
int16 *sampleOut = soundBuffer->samples;
|
int16 *sampleOut = soundBuffer->samples;
|
||||||
for (int sampleIndex = 0; sampleIndex < soundBuffer->sampleCount; sampleIndex++) {
|
for (int sampleIndex = 0; sampleIndex < soundBuffer->sampleCount; sampleIndex++) {
|
||||||
int16 sampleValue = (int16)(sin(state->tSine) * (real32)toneVolume);
|
|
||||||
*sampleOut++ = sampleValue;
|
|
||||||
*sampleOut++ = sampleValue;
|
|
||||||
state->tSine += 2.0f * PI32 / (real32)wavePeriod;
|
state->tSine += 2.0f * PI32 / (real32)wavePeriod;
|
||||||
|
#if 0
|
||||||
|
int16 sampleValue = (int16)(sin(state->tSine) * (real32)toneVolume);
|
||||||
|
#else
|
||||||
|
int16 sampleValue = 0;
|
||||||
|
#endif
|
||||||
|
*sampleOut++ = sampleValue;
|
||||||
|
*sampleOut++ = sampleValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void renderWeirdGradient(GameOffscreenBuffer *buffer, int xOffset, int yOffset) {
|
internal void renderWeirdGradient(GameOffscreenBuffer *buffer, int xOffset, int yOffset) {
|
||||||
int bytesPerPixel = 4;
|
|
||||||
int pitch = buffer->width*bytesPerPixel;
|
|
||||||
uint8 *row = (uint8 *)buffer->memory;
|
uint8 *row = (uint8 *)buffer->memory;
|
||||||
for (int y = 0; y < buffer->height; y++) {
|
for (int y = 0; y < buffer->height; y++) {
|
||||||
uint32 *pixel = (uint32*)row;
|
uint32 *pixel = (uint32*)row;
|
||||||
@@ -26,7 +45,7 @@ internal void renderWeirdGradient(GameOffscreenBuffer *buffer, int xOffset, int
|
|||||||
uint8 green = (uint8)(y + yOffset);
|
uint8 green = (uint8)(y + yOffset);
|
||||||
*pixel++ = (green << 8) | blue;
|
*pixel++ = (green << 8) | blue;
|
||||||
}
|
}
|
||||||
row += pitch;
|
row += buffer->pitch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,11 +65,16 @@ extern "C" GAME_UPDATE_AND_RENDER(gameUpdateAndRender) {
|
|||||||
|
|
||||||
state->toneHz = 440;
|
state->toneHz = 440;
|
||||||
state->tSine = 0;
|
state->tSine = 0;
|
||||||
|
state->playerY = 100;
|
||||||
|
state->playerX = 100;
|
||||||
|
state->blueOffset = 0;
|
||||||
|
state->greenOffset = 0;
|
||||||
memory->isInitialised = true;
|
memory->isInitialised = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int controllerIndex = 0; controllerIndex < ArrayCount(input->controllers); controllerIndex++) {
|
for (int controllerIndex = 0; controllerIndex < ArrayCount(input->controllers); controllerIndex++) {
|
||||||
GameControllerInput *controllerInput = &input->controllers[controllerIndex];
|
GameControllerInput *controllerInput = &input->controllers[controllerIndex];
|
||||||
|
/*
|
||||||
if (controllerInput->isAnalog) {
|
if (controllerInput->isAnalog) {
|
||||||
state->toneHz = 440 + (int)(128.0f*controllerInput->stickAvgX);
|
state->toneHz = 440 + (int)(128.0f*controllerInput->stickAvgX);
|
||||||
state->greenOffset -= (int)(20.0f*controllerInput->stickAvgX);
|
state->greenOffset -= (int)(20.0f*controllerInput->stickAvgX);
|
||||||
@@ -69,9 +93,18 @@ extern "C" GAME_UPDATE_AND_RENDER(gameUpdateAndRender) {
|
|||||||
state->blueOffset -= 10;
|
state->blueOffset -= 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (controllerInput->btnDown.endedDown) {
|
||||||
|
state->playerY -= 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->playerX += (int)(4.0f*controllerInput->stickAvgX);
|
||||||
|
state->playerY -= (int)(4.0f*controllerInput->stickAvgY);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderWeirdGradient(videoBuf, state->greenOffset, state->blueOffset);
|
renderWeirdGradient(videoBuf, state->greenOffset, state->blueOffset);
|
||||||
|
renderPlayer(videoBuf, state->playerX, state->playerY);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" GAME_GET_SOUND_SAMPLES(gameGetSoundSamples) {
|
extern "C" GAME_GET_SOUND_SAMPLES(gameGetSoundSamples) {
|
||||||
|
|||||||
@@ -97,6 +97,8 @@ struct GameOffscreenBuffer {
|
|||||||
void *memory;
|
void *memory;
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
|
int bytesPerPixel;
|
||||||
|
int pitch; // Bytes per row
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GameButtonState {
|
struct GameButtonState {
|
||||||
@@ -151,6 +153,8 @@ struct GameState {
|
|||||||
int greenOffset;
|
int greenOffset;
|
||||||
int blueOffset;
|
int blueOffset;
|
||||||
real32 tSine;
|
real32 tSine;
|
||||||
|
int playerY;
|
||||||
|
int playerX;
|
||||||
};
|
};
|
||||||
|
|
||||||
// === Game to platform services ===
|
// === Game to platform services ===
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ internal void resizeDIBSection(Win32OffscreenBuffer *buffer, int width, int heig
|
|||||||
buffer->width = width;
|
buffer->width = width;
|
||||||
buffer->height = height;
|
buffer->height = height;
|
||||||
buffer->bytesPerPixel = 4;
|
buffer->bytesPerPixel = 4;
|
||||||
|
buffer->pitch = buffer->width*buffer->bytesPerPixel;
|
||||||
|
|
||||||
buffer->info.bmiHeader.biSize = sizeof(buffer->info.bmiHeader);
|
buffer->info.bmiHeader.biSize = sizeof(buffer->info.bmiHeader);
|
||||||
buffer->info.bmiHeader.biWidth = buffer->width;
|
buffer->info.bmiHeader.biWidth = buffer->width;
|
||||||
@@ -359,7 +360,56 @@ internal void win32FillSoundBuffer(Win32SoundOutput *soundOutput, DWORD byteToLo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void win32ProcessPendingMessages(GameControllerInput *keyboardController) {
|
internal void win32BeginRecordingInput(Win32State *win32State, int inputRecordingIndex) {
|
||||||
|
win32State->inputRecordingIndex = inputRecordingIndex;
|
||||||
|
char *filename = "recording.hmi";
|
||||||
|
win32State->recordingHandle = CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, NULL, NULL);
|
||||||
|
DWORD bytesToWrite = (DWORD)win32State->gameMemoryTotalSize;
|
||||||
|
Assert(bytesToWrite < 0xFFFFFFFF);
|
||||||
|
DWORD bytesWritten;
|
||||||
|
WriteFile(win32State->recordingHandle, win32State->gameMemoryBlock, bytesToWrite, &bytesWritten, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void win32EndRecordingInput(Win32State *win32State) {
|
||||||
|
CloseHandle(win32State->recordingHandle);
|
||||||
|
win32State->inputRecordingIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void win32RecordInput(Win32State *win32State, GameInput *newInput) {
|
||||||
|
DWORD bytesWritten;
|
||||||
|
WriteFile(win32State->recordingHandle, newInput, sizeof(*newInput), &bytesWritten, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void win32BeginInputPlayback(Win32State *win32State, int inputPlayingIndex) {
|
||||||
|
win32State->inputPlayingIndex = inputPlayingIndex;
|
||||||
|
char *filename = "recording.hmi";
|
||||||
|
win32State->playbackHandle = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
|
||||||
|
DWORD bytesToRead = (DWORD)win32State->gameMemoryTotalSize;
|
||||||
|
Assert(bytesToRead < 0xFFFFFFFF);
|
||||||
|
DWORD bytesRead;
|
||||||
|
ReadFile(win32State->playbackHandle, win32State->gameMemoryBlock, bytesToRead, &bytesRead, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void win32EndInputPlayback(Win32State *win32State) {
|
||||||
|
CloseHandle(win32State->playbackHandle);
|
||||||
|
win32State->inputPlayingIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void win32PlaybackInput(Win32State *win32State, GameInput *newInput) {
|
||||||
|
DWORD bytesRead;
|
||||||
|
if (ReadFile(win32State->playbackHandle, newInput, sizeof(*newInput), &bytesRead, NULL)) {
|
||||||
|
if (bytesRead == 0) {
|
||||||
|
// Restart
|
||||||
|
int playingIndex = win32State->inputPlayingIndex;
|
||||||
|
win32EndInputPlayback(win32State);
|
||||||
|
win32BeginInputPlayback(win32State, playingIndex);
|
||||||
|
} else {
|
||||||
|
// there's still input, read next input
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void win32ProcessPendingMessages(Win32State *win32State, GameControllerInput *keyboardController) {
|
||||||
MSG message;
|
MSG message;
|
||||||
while (PeekMessageA(&message, NULL, NULL, NULL, PM_REMOVE)) {
|
while (PeekMessageA(&message, NULL, NULL, NULL, PM_REMOVE)) {
|
||||||
if (message.message == WM_QUIT) {
|
if (message.message == WM_QUIT) {
|
||||||
@@ -397,6 +447,15 @@ internal void win32ProcessPendingMessages(GameControllerInput *keyboardControlle
|
|||||||
} else if (VKCode == VK_RIGHT) {
|
} else if (VKCode == VK_RIGHT) {
|
||||||
win32ProcessKeyboardKeypress(&keyboardController->btnRight, isDown);
|
win32ProcessKeyboardKeypress(&keyboardController->btnRight, isDown);
|
||||||
} else if (VKCode == VK_SPACE) {
|
} else if (VKCode == VK_SPACE) {
|
||||||
|
} else if (VKCode == 'L') {
|
||||||
|
if (isDown) {
|
||||||
|
if (win32State->inputRecordingIndex == 0) {
|
||||||
|
win32BeginRecordingInput(win32State, 1);
|
||||||
|
} else {
|
||||||
|
win32EndRecordingInput(win32State);
|
||||||
|
win32BeginInputPlayback(win32State, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,12 +489,11 @@ inline real32 win32GetSecondsElapsed(LARGE_INTEGER start, LARGE_INTEGER end) {
|
|||||||
#define monitorRefreshHz 60
|
#define monitorRefreshHz 60
|
||||||
#define gameUpdateHz (monitorRefreshHz / 2)
|
#define gameUpdateHz (monitorRefreshHz / 2)
|
||||||
|
|
||||||
internal void win32DebugDrawVertical(Win32OffscreenBuffer *screenBuffer, int x, int top, int bottom, int32 color) {
|
internal void win32DebugDrawVertical(Win32OffscreenBuffer *buffer, int x, int top, int bottom, int32 color) {
|
||||||
int pitch = screenBuffer->width*screenBuffer->bytesPerPixel;
|
uint8 *pixel = (uint8 *)buffer->memory + top * buffer->pitch + x*buffer->bytesPerPixel;
|
||||||
uint8 *pixel = (uint8 *)screenBuffer->memory + top * pitch + x*screenBuffer->bytesPerPixel;
|
|
||||||
for (int y = top; y < bottom; y++) {
|
for (int y = top; y < bottom; y++) {
|
||||||
*(uint32 *)pixel = color;
|
*(uint32 *)pixel = color;
|
||||||
pixel += pitch;
|
pixel += buffer->pitch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,26 +503,27 @@ inline void win32DrawSoundBufferMarker(Win32OffscreenBuffer *buffer, Win32SoundO
|
|||||||
win32DebugDrawVertical(buffer, x, top, bottom, color);
|
win32DebugDrawVertical(buffer, x, top, bottom, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void win32DebugSyncDisplay(Win32OffscreenBuffer *screenBuffer, int markerCount, Win32DebugTimeMarker *markers, Win32SoundOutput *soundOutput, real32 targetSecondsPerFrame) {
|
internal void win32DebugSyncDisplay(Win32OffscreenBuffer *buffer, int markerCount, Win32DebugTimeMarker *markers, Win32SoundOutput *soundOutput, real32 targetSecondsPerFrame) {
|
||||||
int padX = 16;
|
int padX = 16;
|
||||||
int padY = 16;
|
int padY = 16;
|
||||||
|
|
||||||
int top = padY;
|
int top = padY;
|
||||||
int bottom = screenBuffer->height - padY;
|
int bottom = buffer->height - padY;
|
||||||
int renderWidth = screenBuffer->width - 2 *padX;
|
int renderWidth = buffer->width - 2 *padX;
|
||||||
|
|
||||||
real32 pxPerSoundBufferEntry = (real32)renderWidth / (real32)soundOutput->secondaryBufferSize;
|
real32 pxPerSoundBufferEntry = (real32)renderWidth / (real32)soundOutput->secondaryBufferSize;
|
||||||
int pitch = screenBuffer->width*screenBuffer->bytesPerPixel;
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
for (int markerIndex = 0; markerIndex < markerCount; markerIndex++) {
|
for (int markerIndex = 0; markerIndex < markerCount; markerIndex++) {
|
||||||
Win32DebugTimeMarker *thisMarker = &markers[markerIndex];
|
Win32DebugTimeMarker *thisMarker = &markers[markerIndex];
|
||||||
real32 alpha = ((real32)(markerIndex + 1) / (real32)markerCount);
|
real32 alpha = ((real32)(markerIndex + 1) / (real32)markerCount);
|
||||||
int x = padX + (int)(pxPerSoundBufferEntry * (real32)thisMarker->writeCursor);
|
int x = padX + (int)(pxPerSoundBufferEntry * (real32)thisMarker->writeCursor);
|
||||||
uint8 *pixel = (uint8 *)screenBuffer->memory + top * pitch + x*screenBuffer->bytesPerPixel;
|
uint8 *pixel = (uint8 *)buffer->memory + top * buffer->pitch + x*buffer->bytesPerPixel;
|
||||||
uint32 newPixel = (uint32)((real32)alpha * 0xFFFFFFFF + (1.0f - alpha) * (*(uint32 *)pixel));
|
uint32 newPixel = (uint32)((real32)alpha * 0xFFFFFFFF + (1.0f - alpha) * (*(uint32 *)pixel));
|
||||||
win32DrawSoundBufferMarker(screenBuffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->playCursor, newPixel);
|
win32DrawSoundBufferMarker(buffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->playCursor, newPixel);
|
||||||
win32DrawSoundBufferMarker(screenBuffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->writeCursor, 0x00FF0000);
|
win32DrawSoundBufferMarker(buffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->writeCursor, 0x00FF0000);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void catStrings(size_t sourceACount, char *sourceA, size_t sourceBCount, char *sourceB, size_t destCount, char *dest) {
|
void catStrings(size_t sourceACount, char *sourceA, size_t sourceBCount, char *sourceB, size_t destCount, char *dest) {
|
||||||
@@ -536,6 +595,8 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
|
|||||||
RegisterHotKey(window, HH_CTRLW, MOD_CONTROL, 'W');
|
RegisterHotKey(window, HH_CTRLW, MOD_CONTROL, 'W');
|
||||||
|
|
||||||
globalRunning = true;
|
globalRunning = true;
|
||||||
|
Win32State win32State = {};
|
||||||
|
|
||||||
GameInput input[2] = {};
|
GameInput input[2] = {};
|
||||||
GameInput *oldInput = &input[0];
|
GameInput *oldInput = &input[0];
|
||||||
GameInput *newInput = &input[1];
|
GameInput *newInput = &input[1];
|
||||||
@@ -568,8 +629,9 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
|
|||||||
gameMemory.debugReadEntireFile = debugReadEntireFile;
|
gameMemory.debugReadEntireFile = debugReadEntireFile;
|
||||||
gameMemory.debug_printf = debug_printf;
|
gameMemory.debug_printf = debug_printf;
|
||||||
|
|
||||||
uint64 totalSize = gameMemory.permanentStorageSize + gameMemory.transientStorageSize;
|
win32State.gameMemoryTotalSize = gameMemory.permanentStorageSize + gameMemory.transientStorageSize;
|
||||||
gameMemory.permanentStorage = VirtualAlloc(baseAddress, totalSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
win32State.gameMemoryBlock = VirtualAlloc(baseAddress, win32State.gameMemoryTotalSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||||
|
gameMemory.permanentStorage = win32State.gameMemoryBlock;
|
||||||
gameMemory.transientStorage = ((uint8 *)gameMemory.permanentStorage + gameMemory.permanentStorageSize);
|
gameMemory.transientStorage = ((uint8 *)gameMemory.permanentStorage + gameMemory.permanentStorageSize);
|
||||||
|
|
||||||
win32InitSound(window, soundOutput.samplesPerSecond, soundOutput.secondaryBufferSize);
|
win32InitSound(window, soundOutput.samplesPerSecond, soundOutput.secondaryBufferSize);
|
||||||
@@ -602,7 +664,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
|
|||||||
newKeyboardController->buttons[buttonIndex].endedDown = oldKeyboardController->buttons[buttonIndex].endedDown;
|
newKeyboardController->buttons[buttonIndex].endedDown = oldKeyboardController->buttons[buttonIndex].endedDown;
|
||||||
}
|
}
|
||||||
|
|
||||||
win32ProcessPendingMessages(newKeyboardController);
|
win32ProcessPendingMessages(&win32State, newKeyboardController);
|
||||||
|
|
||||||
XINPUT_STATE controllerState;
|
XINPUT_STATE controllerState;
|
||||||
int maxControllerCount = XUSER_MAX_COUNT;
|
int maxControllerCount = XUSER_MAX_COUNT;
|
||||||
@@ -670,6 +732,16 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin
|
|||||||
videoBuffer.memory = globalBackBuffer.memory;
|
videoBuffer.memory = globalBackBuffer.memory;
|
||||||
videoBuffer.width = globalBackBuffer.width;
|
videoBuffer.width = globalBackBuffer.width;
|
||||||
videoBuffer.height = globalBackBuffer.height;
|
videoBuffer.height = globalBackBuffer.height;
|
||||||
|
videoBuffer.pitch = globalBackBuffer.pitch;
|
||||||
|
videoBuffer.bytesPerPixel = globalBackBuffer.bytesPerPixel;
|
||||||
|
|
||||||
|
if (win32State.inputRecordingIndex) {
|
||||||
|
win32RecordInput(&win32State, newInput);
|
||||||
|
}
|
||||||
|
if (win32State.inputPlayingIndex) {
|
||||||
|
win32PlaybackInput(&win32State, newInput);
|
||||||
|
}
|
||||||
|
|
||||||
game.updateAndRender(&gameMemory, &videoBuffer, newInput);
|
game.updateAndRender(&gameMemory, &videoBuffer, newInput);
|
||||||
|
|
||||||
DWORD playCursor = 0;
|
DWORD playCursor = 0;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ struct Win32OffscreenBuffer {
|
|||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
int bytesPerPixel;
|
int bytesPerPixel;
|
||||||
|
int pitch;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Win32WindowDimensions {
|
struct Win32WindowDimensions {
|
||||||
@@ -27,3 +28,17 @@ struct Win32DebugTimeMarker {
|
|||||||
DWORD playCursor;
|
DWORD playCursor;
|
||||||
DWORD writeCursor;
|
DWORD writeCursor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Win32RecordedInput {
|
||||||
|
int inputCount;
|
||||||
|
GameInput *inputStream;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Win32State {
|
||||||
|
HANDLE recordingHandle;
|
||||||
|
uint32 inputRecordingIndex;
|
||||||
|
HANDLE playbackHandle;
|
||||||
|
uint32 inputPlayingIndex;
|
||||||
|
uint64 gameMemoryTotalSize;
|
||||||
|
void *gameMemoryBlock;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user