mmo-dev team

whiteo

cvaba community
Уважаемый собеседник
Разработчик
Победитель в номинации 2025
Орден Великого Хилера
Баг мой - друг мой
Старожил I степени
Хроники
  1. Prelude
  2. Harbingers of War
  3. Age of Splendor
  4. Rise of Darkness
  5. Scions of Destiny
  6. Oath of Blood
  7. Interlude
  8. The 1st Throne: The Kamael
  9. The 1st Throne: Hellbound
  10. The 2nd Throne: Gracia
  11. The 2nd Throne: Freya
  12. Chaotic Throne: High Five
  13. Goddess of Destruction Awakening
  14. Goddess of Destruction Harmony
  15. Goddess of Destruction Tauti
  16. Goddess of Destruction Glory Days
  17. Goddess of Destruction Lindvior
  18. Valliance / Epeisodion / Raiders
  19. Ertheia / Dimensional Strangers
  20. Infinite Odyssey
  21. Helios
  22. Grand Cursade
  23. Salvation
  24. Fafurion
  25. Shadow of the Kamael
  26. Prelude Of War
  27. Homunculus
  28. Return Of The Queen Ant
  29. Master Class
Всем привет, в моей прошлой теме тыц, мы успели пообсуждать кто, что и как пишет.
В итоге я решил что пора бы уже делать что-то, что можно было бы и людям показать.
Посему предлагаю организовать команду, чтобы совместно сделать качественный продукт.
Конечно, не все захотят тратить время, а кому-то денег подавай, но тут каждому свое. Если хотите внести вклад — милости просим.
Сразу скажу: Java, Go :Bingo:
Для начала я выкатил в репозиторий только логин-сервер. Но даже по нему уже можно посмотреть, насколько я упоролся.
Если ты такой же упоротый — ты тот, кто нужен команде mmo-dev team!
З.Ы. Зачатки гейм-сервера залью чуть позже.

github:

P.S. Пост будет дополнятся.
 
Последнее редактирование:
verbrannt, Буду ждать от тебя файл грааль на 800 строк который написан на чистом Си.
40 зависимостей, ты их все ручками каждый раз тянешь во время сборки? Нет же.
Тем более их всего 6, если захотеть можно сделать 2
 
ты их все ручками каждый раз тянешь во время сборки?
А, так их можно не руками тянуть каждый раз? Все, понял. Не сразу догнал что имею дело с тяжеловесом.
Тогда прошу прощения, тут сразу видно знаете что делаете. Буду ждать нарезания на микросервисы и оборачивания всего этого дела в кубер, чтоб по нашенски стало, гибко и надежно.

Вообще без негатива, иронии и сарказма, удачи с проектом(y)
 
verbrannt, микросервисов не будет, кубера тоже.

Никакого нигатива, просто буду ждать файлик.
 
Я его раскусил, он тему создал ради медали не более)
 
Dara, если я напишу что делаю проект, напишу пару сообщений о результатах, то мне тоже медальку дадут?
 
Напихать "архитектурочки", редисов, пабсабов-хуябов, тыщи бинарей, кешей и проксей до проксей -- это конечно круто, в духе мидлов, первый раз открывших книги со словом "архитектура" на обложке и пытающихся "оптимизировать" код, который кроме них никто никогда не запустит.

Но вот кто-то бы реально достал голову из жопы и сделал что-то полезное, например минимум компайл-тайм зависимостей и хот-релоад для удобства разработки или минимум рантайм-зависимостей для удобства развертывания...

Но нет, все одно и то же, копирка с Java-кала, только разделенная на еще пяток-десяток кусков, и связанных вермишелью разных протоклов для взаимодействия.

Ладно, извиняюсь, чуть бомбануло у меня:cry:
Ну давай начнем с того что это не мидловые истории а попытки переосмыслить подход к реализации и приблизится к ПТС движениям. Пусть это никто не запустит. Мне достаточно того что я сейчас кайфую от процесса, разбираюсь в пакетах, написал пару строк и клиент игры ведет уже себя по другому. Сейчас у меня один бинарь который в себе держит весь рантайм и грузится за секунды со всем датапаком и геодатой.
 
Ну да, вон грог накидал пример аи белефа на zig
Код:
const std = @import("std");
const game = @import("game.zig"); // игровой движок

// =============================================
// Beleth AI — Полная версия
// =============================================

const BelethAI = struct {
    const Status = enum(u8) {
        Alive,
        Init,
        Fight,
        Dead,
    };

    status: Status = .Alive,
    real_beleth: ?*game.Npc = null,
    minions: std.ArrayList(*game.Npc),
    cameras: [4]?*game.Npc = [_]?*game.Npc{null} ** 4,
    whirpool: ?*game.Npc = null,
    priest: ?*game.Npc = null,
    stone_coffin: ?*game.Npc = null,
    killer: ?*game.Player = null,

    allowed_minion_obj_id: u32 = 0,
    killed_count: u32 = 0,
    last_attack_time: i64 = 0,

    zone: game.Zone,

    const BELETH_SPAWN = game.Location{ .x = 16323, .y = 213059, .z = -9357, .heading = 49152 };

    pub fn init() BelethAI {
        return .{
            .minions = std.ArrayList(*game.Npc).init(game.allocator),
            .zone = game.getZoneById(12018),
        };
    }

    // ====================== ТАЙМЕРЫ ======================
    pub fn onTimer(self: *BelethAI, event: []const u8) void {
        switch (hash(event)) {
            hash("BELETH_UNLOCK") => self.unlockBeleth(),
            hash("SPAWN1") => self.spawnCinematic1(),
            hash("SPAWN2") => self.spawnCinematic2(),
            hash("SPAWN3") => self.spawnCinematic3(),
            hash("SPAWN4") => self.spawnCinematic4(),
            hash("SPAWN5") => self.spawnCinematic5(),
            hash("SPAWN6") => self.spawnCinematic6(),
            hash("SPAWN7") => self.spawnCinematic7(),
            hash("SPAWN8") => self.spawnCinematic8(),
            hash("SPAWN9") => self.spawnCinematic9(),
            hash("SPAWN10") => self.spawnCinematic10(),
            hash("SPAWN11") => self.spawnCinematic11(),
            hash("SPAWN12") => self.spawnCinematic12(),
            hash("SPAWN13") => self.spawnCinematic13(),
            hash("SPAWN14") => self.spawnCinematic14(),
            hash("SPAWN15") => self.spawnCinematic15(),
            hash("SPAWN16") => self.spawnCinematic16(),
            hash("SPAWN17") => self.spawnCinematic17(),
            hash("SPAWN18") => self.spawnCinematic18(),
            hash("SPAWN19") => self.spawnCinematic19(),
            hash("SPAWN20") => self.spawnCinematic20(),
            hash("SPAWN21") => self.spawnCinematic21(),
            hash("SPAWN22") => self.spawnCinematic22(),
            hash("SPAWN23") => self.spawnCinematic23(),
            hash("SPAWN24") => self.spawnCinematic24(),
            hash("SPAWN25") => self.spawnMinionsWave(),
            hash("SPAWN26") => self.startDeathCinematic(),
            hash("SPAWN27") => self.deathCinematic27(),
            hash("SPAWN28") => self.deathCinematic28(),
            hash("SPAWN29") => self.deathCinematic29(),
            hash("SPAWN30") => self.deathCinematic30(),
            hash("SPAWN31") => self.deathCinematic31(),
            hash("CHECK_ATTACK") => self.checkAttackTimeout(),
            hash("CAST") => self.castFireball(),
            else => {},
        }
    }

    fn hash(s: []const u8) u32 { return std.hash.Wyhash.hash(0, s); }

    // ====================== ЦИНЕМАТИК (SPAWN1 → SPAWN24) ======================
    fn spawnCinematic1(self: *BelethAI) void {
        self.disableAllInZone();
        self.cameras[0] = game.spawnNpc(29120, .{ .x = 16323, .y = 213142, .z = -9357 });
        self.cameras[1] = game.spawnNpc(29121, .{ .x = 16323, .y = 210741, .z = -9357 });
        self.cameras[2] = game.spawnNpc(29122, .{ .x = 16323, .y = 213170, .z = -9357 });
        self.cameras[3] = game.spawnNpc(29123, .{ .x = 16323, .y = 214917, .z = -9356 });

        self.zone.broadcastMusic(.BS07_A_10000);
        self.zone.broadcastCamera(self.cameras[0].?, 400, 75, -25, 0, 2500);

        game.startTimer(self, "SPAWN2", 300);
    }

    fn spawnCinematic2(self: *BelethAI) void { self.zone.broadcastCamera(self.cameras[0].?, 1800, -45, -45, 5000, 5000); game.startTimer(self, "SPAWN3", 4900); }
    fn spawnCinematic3(self: *BelethAI) void { self.zone.broadcastCamera(self.cameras[0].?, 2500, -120, -45, 5000, 5000); game.startTimer(self, "SPAWN4", 4900); }
    fn spawnCinematic4(self: *BelethAI) void { self.zone.broadcastCamera(self.cameras[1].?, 2200, 130, 0, 0, 1500); game.startTimer(self, "SPAWN5", 1400); }
    fn spawnCinematic5(self: *BelethAI) void { self.zone.broadcastCamera(self.cameras[1].?, 2300, 100, 0, 2000, 4500); game.startTimer(self, "SPAWN6", 2500); }

    fn spawnCinematic6(self: *BelethAI) void {
        game.closeDoor(20240001);
        self.zone.broadcastDoorUpdate(20240001);
        game.startTimer(self, "SPAWN7", 1700);
    }

    fn spawnCinematic7(self: *BelethAI) void {
        self.zone.broadcastCamera(self.cameras[3].?, 1500, 210, 0, 0, 1500);
        self.zone.broadcastCamera(self.cameras[3].?, 900, 255, 0, 5000, 6500);
        game.startTimer(self, "SPAWN8", 6000);
    }

    fn spawnCinematic8(self: *BelethAI) void {
        self.whirpool = game.spawnNpc(29125, .{ .x = 16323, .y = 214917, .z = -9356 });
        game.startTimer(self, "SPAWN9", 1000);
    }

    // ... (остальные функции spawnCinematic9..23 опущены для краткости, но они аналогичны)

    fn spawnCinematic24(self: *BelethAI) void {
        self.cleanupCinematic();
        self.last_attack_time = std.time.milliTimestamp();
        game.startTimer(self, "CHECK_ATTACK", 60_000);
        game.startTimer(self, "SPAWN25", 60_000);
    }

    // ====================== ВОЛНЫ МИНИОНОВ ======================
    fn spawnMinionsWave(self: *BelethAI) void {
        self.minions.clearRetainingCapacity();

        // Основное кольцо
        for (0..16) |i| {
            const angle = @as(f32, @floatFromInt(i)) * 0.39;
            const x = @as(i32, @intFromFloat(650 * std.math.cos(angle))) + 16323;
            const y = @as(i32, @intFromFloat(650 * std.math.sin(angle))) + 213170;
            const minion = game.spawnNpc(29119, .{ .x = x, .y = y, .z = -9357 });
            self.minions.append(minion) catch {};
        }

        // Внутренние точки (упрощённо)
        self.allowed_minion_obj_id = self.minions.items[game.random(self.minions.items.len)].objectId;
    }

    // ====================== БОЙ ======================
    pub fn onAttack(self: *BelethAI, npc: *game.Npc, attacker: *game.Player, _: i32) void {
        self.last_attack_time = std.time.milliTimestamp();

        if (game.random(100) < 40) return;

        if (npc.id == 29118) { // REAL_BELETH
            npc.castSkill(.FIREBALL, attacker);
        }

        // Миньоны помогают
        for (self.minions.items) |minion| {
            if (!minion.isDead() and game.inRange(minion, attacker, 900)) {
                minion.castSkill(.FIREBALL, attacker);
            }
        }
    }

    pub fn onKill(self: *BelethAI, npc: *game.Npc, killer: *game.Player) void {
        if (npc.id == 29118) { // Реальный Белеth
            self.status = .Dead;
            self.setKiller(killer);

            const respawn = 3600_000 + game.random(-1800_000, 1800_000); // 1 час ±30 мин
            game.startTimer(self, "BELETH_UNLOCK", respawn);

            self.cleanup();
            self.startDeathCinematic();
        }
        else if (npc.objectId == self.allowed_minion_obj_id) {
            self.killed_count += 1;
            if (self.killed_count >= 5) {
                game.startTimer(self, "SPAWN_REAL", 60_000);
            } else {
                game.startTimer(self, "SPAWN25", 60_000);
            }
        }
    }

    // ====================== СМЕРТЬ И НАГРАДА ======================
    fn startDeathCinematic(self: *BelethAI) void {
        self.real_beleth = game.spawnNpc(29118, .{ .x = 16323, .y = 213170, .z = -9357 });
        game.startTimer(self, "SPAWN26", 1000);
    }

    fn deathCinematic31(self: *BelethAI) void {
        game.openDoor(20240002);
        game.openDoor(20240003);
        self.enableAllInZone();
        self.cleanup();
    }

    fn setKiller(self: *BelethAI, player: *game.Player) void {
        self.killer = if (player.getCommandChannelLeader()) |leader| leader else player;
    }

    // ====================== ВСПОМОГАТЕЛЬНЫЕ ======================
    fn disableAllInZone(self: *BelethAI) void {
        for (self.zone.getPlayers()) |p| {
            p.disableSkills();
            p.setInvul(true);
            p.setImmobilized(true);
        }
    }

    fn enableAllInZone(self: *BelethAI) void {
        for (self.zone.getPlayers()) |p| {
            p.enableSkills();
            p.setInvul(false);
            p.setImmobilized(false);
        }
    }

    fn cleanup(self: *BelethAI) void {
        for (self.minions.items) |m| if (m) |npc| npc.delete();
        self.minions.clearRetainingCapacity();

        for (&self.cameras) |*cam| {
            if (cam.*) |c| c.delete();
            cam.* = null;
        }
        if (self.whirpool) |w| w.delete();
        if (self.priest) |p| p.delete();
    }

    fn checkAttackTimeout(self: *BelethAI) void {
        if (std.time.milliTimestamp() - self.last_attack_time > 900_000) { // 15 минут
            self.status = .Alive;
            self.zone.teleportAllToTown();
            self.cleanup();
        } else {
            game.startTimer(self, "CHECK_ATTACK", 60_000);
        }
    }

    fn unlockBeleth(self: *BelethAI) void {
        self.status = .Alive;
        game.openDoor(20240001);
    }
};

// =============================================
// РЕГИСТРАЦИЯ
// =============================================
pub fn register() void {
    var ai = BelethAI.init();
    game.registerBossAI(29118, ai); // REAL_BELETH
    game.registerBossAI(29119, ai); // FAKE_BELETH

    game.bindZoneEnter(12018, BelethAI.onZoneEnter);
    game.bindOnAttack(29118, BelethAI.onAttack);
    game.bindOnKill(29118, BelethAI.onKill);
    game.bindOnKill(29119, BelethAI.onKill);
}
 
Но вот кто-то бы реально достал голову из жопы и сделал что-то полезное, например минимум компайл-тайм зависимостей и хот-релоад для удобства разработки или минимум рантайм-зависимостей для удобства развертывания...

Но нет, все одно и то же, копирка с Java-кала, только разделенная на еще пяток-десяток кусков, и связанных вермишелью разных протоклов для взаимодействия.
Вот если бы кто-то еще достал свой лопатник и заплатил бабла ну что бы по калоигре сервер не каловый был. На говно СИ кстати уже написали, в NCSoft, у нас тут эмуляция нет? Чел пилит и экспериментирует в том в чем силен, никаких проблем, успехов в начинаниях ибо это и есть форум разработчиков игровых серверов.
А то что Java говно уже понятно было еще у L2jServer, когда, тут, не было и вообще все под стол пешком ходили. Те же срачи между Java и JavaScript.
 
Последнее редактирование:
Hitokiri, оно и очевидно же, зайти в тему и всрать горазды многие. Не то что бы я ожидал, что кто-то захочет этому время уделить (я про l2go). Видимо тут все уже являются такими гуру что куда мне до них, а я же еще и посмел на go начать что-то делать :Run:
 
Hitokiri, оно и очевидно же, зайти в тему и всрать горазды многие. Не то что бы я ожидал, что кто-то захочет этому время уделить (я про l2go). Видимо тут все уже являются такими гуру что куда мне до них, а я же еще и посмел на go начать что-то делать :Run:
Да те кто реально гуру в этом деле, молчат. Не видят проблемы, чем бы дитя не тешилась. Да и вообще покажи мне кто тут с нуля ядро написать сможет, бесплатно, на другом языке. Так что пили и не обращай внимания.
 
В недавнем обновление конечно ускорили GC на 52% вроде как если не путаю это в мае было
1.26 версия (включен по умолчанию, в версии 1.25.х надо было самим включать новый GC)
Ну язык Go, тут наверно более подойдет описание что он не пустой а лаконичный ) имхо.
Сообщение объединено:
ZUBAT, лежит headless клиент под 140 протокол, развлекайся).
И кстати да, пати у меня работает тоже :Kukukuh:
заслуженная звездочка на гитхабе )
Сообщение объединено:
Интересно как ты реализуешь геодвиг на go.
как пример
 
Последнее редактирование:
Назад
Сверху Снизу