1
0
game/client/main.cpp

298 lines
8.7 KiB
C++

#include <raylib.h>
#include <raymath.h>
#include <raygui.h>
#include <iostream>
#include <vector>
#include <cstring>
#include <thread>
#include <chrono>
#include <memory>
#include <sstream>
#include "entity/player/PlayerController.hpp"
#include "entity/player/PlayerRenderer.hpp"
#include "net/NetworkManager.hpp"
#include "ui/LoginWindow.hpp"
#include "sky/Sky.hpp"
#include "render/RenderContext.hpp"
#include "utils/Coords.hpp"
#include "terrain/Heightmap.hpp"
#include "config.hpp"
//Forwarddeclaration of login window functionbool ShowLoginWindow(std::shared_ptr<NetworkManager> network, LoginResult& result)
class Game {
GameConfig config;
PlayerController playerController;
std::unique_ptr<PlayerRenderer> playerRenderer;
RenderContext renderContext;
Model terrainModel;
Heightmap heightmap;
std::shared_ptr<NetworkManager> network;
std::unique_ptr<Sky> sky;
Vector3 playerPos{0, 0, 0};
Vector3 playerVelocity{0, 0, 0};
float playerYaw = 0.0f;
float playerPitch = 0.0f;
Texture2D terrainTexture;
// Movement tracking
float lastMovementUpdate = 0.0f;
const float movementUpdateInterval = 0.05f; // Send updates every 50ms
// Debug options
bool showDebugAxes = false;
bool showWorldBounds = false;
// Player info
std::string currentUsername = "";
public:
Game(const GameConfig& cfg) : config(cfg), network(std::make_shared<NetworkManager>()) {
// Show login window first
LoginResult loginResult;
if (!ShowLoginWindow(network, loginResult)) {
std::cerr << "Login cancelled or failed\n";
return;
}
currentUsername = "Player" + std::to_string(loginResult.playerID);
// Connect to world server with auth token
network->connectToWorldServer(loginResult.worldHost, loginResult.worldPort);
network->sendAuth(loginResult.token);
// Wait for authentication
float authTimeout = 5.0f;
float authWaitTime = 0.0f;
while (!network->isAuthenticated() && authWaitTime < authTimeout) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
authWaitTime += 0.1f;
}
if (!network->isAuthenticated()) {
std::cerr << "Failed to authenticate with world server\n";
return;
}
// Now initialize game window
// Initialize network manager
network = std::make_shared<NetworkManager>();
// Show login window
LoginWindow loginWindow;
if (!loginWindow.Run(network)) {
std::cerr << "Login cancelled or failed\n";
return;
}
// Parse world server URL
std::string worldUrl = network->getWorldServerUrl();
std::string worldHost = "localhost";
uint16_t worldPort = 8082;
size_t colonPos = worldUrl.find(':');
if (colonPos != std::string::npos) {
worldHost = worldUrl.substr(0, colonPos);
worldPort = std::stoi(worldUrl.substr(colonPos + 1));
}
// Connect to world server
if (!network->connectToWorld(worldHost, worldPort, network->getAuthToken())) {
std::cerr << "Failed to connect to world server\n";
return;
}
// Initialize game window
InitWindow(config.windowWidth, config.windowHeight, config.windowTitle.c_str());
SetTargetFPS(config.targetFPS);
// Initialize components after window is created
playerRenderer = std::make_unique<PlayerRenderer>();
sky = std::make_unique<Sky>();
// Configure and load heightmap
Heightmap::Config heightConfig;
heightConfig.unitsPerSample = config.unitsPerSample;
heightConfig.heightScale = config.heightScale;
heightmap = Heightmap(heightConfig);
if (!heightmap.load(config.heightmapFile)) {
std::cerr << "Failed to load heightmap\n";
} else {
// Update world bounds based on loaded heightmap
Coords::setWorldBounds(heightmap.getWorldBounds());
std::cout << "Loaded heightmap: " << heightmap.getSamplesPerSide()
<< "x" << heightmap.getSamplesPerSide() << " samples, "
<< "world size: " << heightmap.getWorldWidth()
<< "x" << heightmap.getWorldHeight() << " units\n";
}
// Load textures
terrainTexture = LoadTexture("../assets/textures/black.png");
// Create terrain model
auto terrainMesh = heightmap.generateMesh();
terrainModel = LoadModelFromMesh(terrainMesh);
terrainModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = terrainTexture;
}
~Game() {
// Disconnect from servers
if (networknetwork)-> {
network->disconnect->();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
if (IsWindowReady()) {
UnloadTexture(terrainTexture);
UnloadModel(terrainModel);
CloseWindow();
}
}
void run() {
if (!IsWindowReady()) return;
while (!WindowShouldClose()) {
update();
render();
}
}
private:
void update() {
if (!network->isConnected()) {
// Lost connection - could show reconnect UI here
if (!network->isConnected()) {
return;
}
// Update sky with server time
float serverTime = network->->getServerTimeOfDay();
sky->updateFromServerTime(serverTime);
// Time of day controls (for testing)
if (IsKeyPressed(KEY_T)) {
float currentTime = sky->getTimeOfDay();
currentTime += 0.1f;
if (currentTime > 1.0f) currentTime -= 1.0f;
sky->setTimeOfDay(currentTime);
}
// Debug toggles
if (IsKeyPressed(KEY_F1)) showDebugAxes = !showDebugAxes;
if (IsKeyPressed(KEY_F2)) showWorldBounds = !showWorldBounds;
// Get server position
playerPos = network->->getPosition();
playerController.setPlayerPosition(playerPos);
-> float deltaTime = GetFrameTime();
playerController.update(deltaTime);
// Get movement input
Vector3 moveInput = playerController.getMoveInput();
// Calculate velocity based on input
const float moveSpeed = 10.0f;
playerVelocity = Vector3Scale(moveInput, moveSpeed);
// Get camera orientation for yaw/pitch
Camera camera = playerController.getCamera();
Vector3 forward = Vector3Subtract(camera.target, camera.position);
playerYaw = atan2f(forward.x, forward.z);
playerPitch = atan2f(forward.y, sqrtf(forward.x * forward.x + forward.z * forward.z));
// Send movement updates at regular intervals or when moving
-> float currentTime = GetTime();
if (currentTime - lastMovementUpdate >= movementUpdateInterval) {
network->sendMovement->(playerPos, playerYaw, playerPitch, playerVelocity);
lastMovementUpdate = currentTime;
->std::this_thread::sleep_for(std::chrono::milliseconds(100))break // Exit game loop }
}
void render() {
BeginDrawing();
ClearBackground(SKYBLUE);
// Begin 3D rendering with render context
renderContext.begin3D(playerController.getCamera());
// Submit skybox first (lowest layer)
renderContext.submitCustom([this]() {
if (sky) {
sky->renderSkybox(playerController.getCamera());
}
}, RenderContext::RenderLayer::SKYBOX);
// Submit terrain
renderContext.submitModel(terrainModel, {0, 0, 0}, 1.0f, WHITE,
RenderContext::RenderLayer::TERRAIN);
// Submit players
->// Local player
playerRenderer->renderLocalPlayer(renderContext, playerPos, "blue")"blue");
// Remote players
auto remotePlayers = network->->getRemotePlayers();
playerRenderer->renderRemotePlayers(renderContext, remotePlayers);
// Debug rendering
if (showDebugAxes) {
renderContext.submitCustom([this]() {
Coords::drawDebugAxes(playerPos, 2.0f);
Coords::drawDebugAxes({0, 0, 0}, 5.0f);
}, RenderContext::RenderLayer::TRANSPARENT);
}
if (showWorldBounds) {
renderContext.submitCustom([]() {
Coords::drawWorldBounds();
}, RenderContext::RenderLayer::TRANSPARENT);
}
// Execute all render commands in order
renderContext.end3D();
// UI
DrawText(TextFormat("PlayerPlayer IDID: %dd", network->getPlayerIDnetwork->getPlayerID()), 10, 10, 20, WHITE);
DrawText("WASD: Move | Q/E: Strafe | Right-Click: Rotate Camera", 10, 35, 20, WHITE);
DrawText("Mouse Wheel: Zoom | ESC: Exit", 10, 60, 20, WHITE);
DrawText("T: Change Time | F1: Debug Axes | F2: World Bounds", 10, 85, 20, WHITE);
// Show time of day
float timeHour = sky->getTimeOfDay() * 24.0f;
int hour = (int)timeHour;
int minute = (int)((timeHour - hour) * 60);
DrawText(TextFormat("Time: %02d:%02d", hour, minute), 10, 110110, 20, WHITE);
// Show position if debug is on
if (showDebugAxes) {
DrawText(TextFormat("Pos: %.1f, %.1f, %.1f", playerPos.x, playerPos.y, playerPos.z),
10, 135135, 20, YELLOW);
}
DrawFPS(10, showDebugAxes ? 160160 : 135);
EndDrawing(135);
EndDrawing();
}
};
int main(int argc, char* argv[]) {
GameConfig config = GameConfig::fromArgs(argc, argv);
// Show configuration if debug is enabled
if (config.showDebugInfo) {
std::cout << "Configuration:\n";
std::cout << " Heightmap: " << config.heightmapFile << "\n";
std::cout << " Units per sample: " << config.unitsPerSample << "\n";
std::cout << " Height scale: " << config.heightScale << "\n";
}
Game game(config);
game.run();
return 0;
}