Paylaşım Hywave Survival / SMP & Housing | Geliştirilme Konusu

  • Konuyu Başlatan Konuyu Başlatan Senpai
  • Başlangıç tarihi Başlangıç tarihi
  • Görüntüleme 533
Durum
Üzgünüz bu konu cevaplar için kapatılmıştır...

Senpai

Züm... Zümrü... Zümrüt...
Yasaklandı
Katılım
24 Kasım 2014
Mesajlar
816
Elmaslar
341
Puan
14.845
Yaş
27
Konum
Istanbul
Minecraft
NaN2
Selamlar!

Bu konu, sürekli güncellenen ve içerisinde yaptığım sunucu projesinden teknik bloglar paylaşacağım, küçük büyük demeden geliştirdiğim, yapımı biten bütün sistemleri tanıtacağım bir konu olacak. Şuan "Survival" sunucusu üzerinde duruyorum yani bir süre sadece Survival ile alakalı geliştirici blogları gelecek.
 
Dev Blog #1 - Chat

Eski bir kaç projemde kullandığım ancak asla bitirmediğim bir chat sistemini sonunda bitirdim. Bu blogda neden Minecraft'ın normal chat sisteminden daha kullanışlı olduğunu ve bazı performans kaygılarını ele alacağım .

1) Vanilla (Minecraft) Chatinin eksiklikleri:

Vanilla chatin herhangi bir kullanıcı dostu özelliği yok. Mesajlar çabucak kayboluyor, küfür vb korumasının yeterli kalmadığı zamanlar belirtilen mesajı silme olanağı vermiyor, tonlarca mesaj arasından keyword ile arama olasılığınız yok.


2) Çözüm:

Hywave projesini elimden geldiğince "mükemmele" yakın yapmaya çalışıyorum - her ne kadar mümkün olmasada ve elimden geldiğince küçük detaylara bile dikkat etmeye çalışıyorum.

1) Mesaj Tipleri:
1.1 Sunucu / Sistem Mesajları:
Bu mesajlar oyunculara sistemlerimiz tarafından gönderilen mesajlar. Bütün özellikler bir modül dağıtım sisteminden çıktığı için, kolayca sistem mesajlarını karışıklık olmadan gönderebiliyoruz.
1.2 Oyuncu Mesajları:
Oyuncuların gönderdiği bütün mesajlar.

2) Sistem nasıl çalışıyor?
Sistemin çalışma mantığı oldukça basit. İlk olarak, bir sohbet şablonumuz var. Bazısı dinamik, bazısı stabil olmak üzere bileşenlerimiz var. Misal olarak, sayfa çubuğunun olduğu kısım dinamik. Çünkü sayfa sayılarını, Önceki / Sonraki butonlarının eğer herhangi bir sayfa yoksa renginin değişmesi için dinamik olması gerekiyor. Statik olan kısım üst kısım (videoda göstermeyi unutmuşum) ancak bu üst kısıma "sticky messages" dediğimiz yapışkan mesajları gösterebiliyoruz. Bu bir duyuru olabilir, özel mesaj olabilir, uyarı olabilir, hata mesajı olabilir vesaire.


Kod:
private void sendNavigationBar(Player player, int currentPage, int maxPages) {
        TextComponent bar = new TextComponent("§8Sayfa " + (currentPage + 1) + "/" + maxPages);

        TextComponent prev = new TextComponent(" §8| ");
        prev.addExtra(currentPage > 0
            ? new TextComponent("§7← Önceki")
            : new TextComponent("§8← Önceki"));

        if (currentPage > 0) {
            prev.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/chat " + (currentPage - 1)));
            prev.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
                new ComponentBuilder("§7Önceki sayfaya git").create()));
        }
        
        bar.addExtra(prev);

        boolean isSearching = inSearchMode.getOrDefault(player.getUniqueId(), false);
        TextComponent search = new TextComponent(" §8| ");
        search.addExtra(isSearching
            ? new TextComponent("§cFiltreyi Kaldır")
            : new TextComponent("§eAra"));
        search.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND,
            isSearching ? "/chat clear" : "/chat search"));
        search.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
            new ComponentBuilder(isSearching
                ? "§7Canlı sohbete dön"
                : "§7Mesajlarda ara").create()));
        bar.addExtra(search);

        TextComponent next = new TextComponent(" §8| ");
        next.addExtra(currentPage < maxPages - 1
            ? new TextComponent("§7Sonraki →")
            : new TextComponent("§8Sonraki →"));
        if (currentPage < maxPages - 1) {
            next.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/chat " + (currentPage + 1)));
            next.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
                new ComponentBuilder("§7Sonraki sayfaya git").create()));
        }
        bar.addExtra(next);

        player.spigot().sendMessage(bar);
    }


Şimdi ise; mesajlar nasıl gösteriliyor?:
Bunun da aslında gayet kolay bir mantığı var. Mesaj havuzu gerektiği zaman güncelleniyor ve oyuncuların sohbetleri güncelleniyor. Misal olarak, bir mesaj sadece bir oyuncuya geldiyse, bu oyuncunun mesaj havuzuna o mesajı ekliyoruz. Eğer genel sohbete bir mesaj yazıldıysa, herkesin mesaj havuzuna o mesajı ekliyoruz. Bir mesaj silindiği zaman, havuzdan o mesajı silip tüm oyuncular için sohbeti güncelliyoruz. Tabii ki yapışkan mesajlar ve navigasyon çubuğu yerinde kalacak şekilde.



Performans!
Sistem tamamen performans kaygıları düşünülerek tasarlanmıştır. Küçük bellek kullanımı, verimli işleme optimizasyonları yüksek trafikli sunucular için bile kullanılabilir olmasını sağlıyor.

Demo:
 
Gerçekten harika bir sistem olmuş, tebrik ederim ellerinize sağlık.
 
Açık kaynak paylaşacakmısın yoksa bende yapmayı deneyecegim izinin olursa.
Tabii deneyebilirsin, proje sunucu olarak açılacağı için açık kaynak yapmayı düşünmüyorum ancak zaten anlattığım mantığı kullanırsan kolayca yapabileceğine inanıyorum
 
Tabii deneyebilirsin, proje sunucu olarak açılacağı için açık kaynak yapmayı düşünmüyorum ancak zaten anlattığım mantığı kullanırsan kolayca yapabileceğine inanıyorum
Yapmayı deneyecegim.
 
Dev Blog #2 - Non-Player Character Text Scripting Language (NPCTSL)

Yaptığım projede Housing (insanların kendi arsalarında oyunlar yaratabildiği, hayal gücü sınırlarını zorlayabildiği kadar zorlayabileceği bir oyun modu) için yaptığım NPCTSL (Non-Player Character Text Scripting Language)'yi tanıtacağım.

Normalde Housing sunucum için yaptığım HTSL (Housing Text Scripting Language)'nin bir parçasıydı bu ancak Survival sunucusuna yoğunlaştığım için orada da kullanma kararı aldım.

Parser, metni önce anlamlı parçalara (token) ayırıyor ve bu tokenları hiyerarşik bir yapıya dönüştürüyor. Bu yapı daha sonra NPC ve diyalog nesnelerine dönüştürülüyor. Böylece yazılan kodlar, oyun içinde çalışan gerçek nesnelere çevriliyor.


Bir örnek:


1738280472287.webp








Demo:
 

Ekli dosyalar

  • 1738281925134.webp
    1738281925134.webp
    101,4 KB · Görüntüleme: 78
Son düzenleme:
Dev Blog #2 - Non-Player Character Text Scripting Language (NPCTSL)

Yaptığım projede Housing (insanların kendi arsalarında oyunlar yaratabildiği, hayal gücü sınırlarını zorlayabildiği kadar zorlayabileceği bir oyun modu) için yaptığım NPCTSL (Non-Player Character Text Scripting Language)'yi tanıtacağım.

Normalde Housing sunucum için yaptığım HTSL (Housing Text Scripting Language)'nin bir parçasıydı bu ancak Survival sunucusuna yoğunlaştığım için orada da kullanma kararı aldım.

Parser, metni önce anlamlı parçalara (token) ayırıyor ve bu tokenları hiyerarşik bir yapıya dönüştürüyor. Bu yapı daha sonra NPC ve diyalog nesnelerine dönüştürülüyor. Böylece yazılan kodlar, oyun içinde çalışan gerçek nesnelere çevriliyor.


Bir örnek:


Ekli dosyayı görüntüle 258211







Demo:

hiç fena değil cs:go daki hikayeli atölye maplerine gittim geldim :D
 
Dev Blog #3 - State Machine ve Modüler Sistemler

Bu tamamen teknik bir devblog olacak. Bugün Hywave'nin teknik altyapısının kalbini oluşturan iki önemli sistemi derinlemesine inceleyeceğiz.

1. State Machine​


State Machine sistemimiz, basit bir enum yapısının çok ötesine geçiyor. ServerState enum'u ile başlayan yapımız, ServerStateManager ve ServerStateAPI ile güçlü bir üçlü oluşturuyor. Bu sayede, sunucumuzda oluşan kritik sorunlar (kendi hata ayıklama sistemimizi kullanıyoruz) oyuncuların oyun keyfi bozulmadan, veya ileride herhangi bir rollback (geriye sarma) gerekmeden sunucumuzu erişilmez hale getiriyor.

Örneğin:

Kod:
ServerStateAPI.addStateChangeListener((oldState, newState) -> {
    if (newState == ServerState.SEVERE_ERROR_IN_ECONOMY_MODULE) {
        // Veritabanına yeni sunucu durumunu kaydet
        // Web paneli güncelle
        // MOTD güncelle
        // Tüm oyuncuları sunucudan at
    }
});

Bu sayede, sunucumuzda oluşan kritik sorunlar (ekonomi ve diğer önemli parçalar) sunucumuza zarar veremiyor.

2. Modüler Sistemler​

Tüm sistemler ManagerRegistry üzerinden merkezi olarak yönetiliyor, bunun bize sağladığı en önemli özellik: sıkıntı çıkartan modülleri sunucuyu yeniden başlatmadan güncelleyebilmek, kapatabilmek veya bazı gruplara erişilmez hale getirmek. Bu sayede, kritik olmayan modüllerde (mesela ekonomi) sunucudaki oyuncuların keyfi bozulmadan güncelleme getirebiliyoruz.

Örnek:

Kod:
managerRegistry.registerManager(new MotdManager(plugin)); // Sistemi kaydet

// Manager'a erişim
MotdManager motdManager = managerRegistry.getManager(MotdManager.class); // Sisteme eriş
managerRegistry.getManager(EconomyManager.class).disable(); // Sistemi devre dışı bırak

Modül Örneği:
Kod:
package com.hywave.gameserver.core.managers.motd;

import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketEvent;

import com.hywave.gameserver.Base;
import com.hywave.gameserver.core.managers.Manager;
import com.hywave.gameserver.core.statemachine.ServerState;
import com.hywave.gameserver.core.statemachine.api.ServerStateAPI;

import org.bukkit.ChatColor;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.server.ServerListPingEvent;

import com.google.common.base.Strings;

public class MotdManager extends Manager implements Listener {
    private static final String HEADER = "&6&lPLAY.HYWAVE.NET &7[Survival]";
    private static final String HEADER_UNV = "&8&lPLAY.HYWAVE.NET";
    private static final String VER_MSG = "&cDesteklenmeyen versiyon. Lütfen Minecraft &f1.19.4 &cile giriş yapın!";

    private static final int PROTOCOL_1_19_4 = 762;

    private boolean versiyonKontrol = false;

    public MotdManager(Base plugin) {
        super(plugin);
        setupProtocolLib();
    }

    @Override
    public void onEnable() {
        plugin.getServer().getPluginManager().registerEvents(this, plugin);
    }

    @Override
    public void onDisable() {
        ServerListPingEvent.getHandlerList().unregister(this);
        ProtocolLibrary.getProtocolManager().removePacketListeners(plugin);
    }

    @Override
    public void onReload() {
    }

    private void setupProtocolLib() {
        ProtocolLibrary.getProtocolManager().addPacketListener(
            new PacketAdapter(plugin, PacketType.Handshake.Client.SET_PROTOCOL) {
                @Override
                public void onPacketReceiving(PacketEvent event) {
                    int version = event.getPacket().getIntegers().read(0);
                    versiyonKontrol = version < PROTOCOL_1_19_4;
                }
            }
        );
    }

    @EventHandler
    public void onServerPing(ServerListPingEvent event) {
        ServerState _state = ServerStateAPI.getCurrentState();
        StringBuilder motd = new StringBuilder();
        
        boolean mevcut = _state == ServerState.RUNNING && !versiyonKontrol;
        String header = mevcut ? HEADER : HEADER_UNV;
        
        motd.append(centerText(header)).append("\n");
        
        if (versiyonKontrol) {
            event.setMaxPlayers(0);
            motd.append(centerText(VER_MSG));
            event.setMotd(colorize(motd.toString()));
            return;
        }

        switch (_state) {
            case MAINTENANCE:
                motd.append(centerText("&c&lBAKIMDA"));
                event.setMaxPlayers(0);
                break;
            case WHITELISTED:
                motd.append(centerText("&e&lERİŞİLMEZ"));
                break;
            case RESTARTING:
                motd.append(centerText("&6&lYENİDEN BAŞLATILIYOR"));
                event.setMaxPlayers(0);
                break;
            case STOPPING:
                motd.append(centerText("&c&lDURDURULUYOR"));
                event.setMaxPlayers(0);
                break;
            default:
                motd.append(centerText(String.format("&e&lMaceraya hazır mısın?")));
                break;
        }

        event.setMotd(colorize(motd.toString()));
    }

    private static String centerText(String text, String rightAligned) {
        int _legacy_ml = 55;
        String _clt = colorize(text);
        String _cltr = colorize(rightAligned);
        
        int _mL = ChatColor.stripColor(_clt).length();
        int _rL = ChatColor.stripColor(_cltr).length();
        
        int _tcl = _mL + _rL;
        int _ls = (_legacy_ml - _tcl) / 2;
        int _rs = _legacy_ml - (_ls + _tcl);
        
        return Strings.repeat(" ", _ls) +
                _clt +
               Strings.repeat(" ", _rs) +
               _cltr;
    }

    private static String centerText(String text) {
        int _legacy_ml = 55;
        text = colorize(text);
        int _l = ChatColor.stripColor(text).length();
        int _b = (_legacy_ml - _l) / 2;
        
        return Strings.repeat(" ", Math.max(0, _b)) + text;
    }

    private static String colorize(String text) {
        return ChatColor.translateAlternateColorCodes('&', text);
    }
}
















 
Diyalog sistemi için bı eklenti vardı eğer açık kaynak kodluysa oradan örnek alabilirsin diyaloğun İngilizce sini yazınca çıkıyor BBB ya da spigotta su an link atamiyorum
 
Diyalog sistemi için bı eklenti vardı eğer açık kaynak kodluysa oradan örnek alabilirsin diyaloğun İngilizce sini yazınca çıkıyor BBB ya da spigotta su an link atamiyorum
NPC kütüphanesi ve diyalog sistemi biteli çok oluyor, dev blogda var
 
Sunucuyu ne olur ne olmaz Türkçe / İngilizce yapıyorum çünkü verdiğim emeğin karşılığını Türkiye'de alamayacağıma neredeyse adım gibi eminim, oyuncu kitlesinin çoğu ne olduğunu bile anlamadan girip çıkacak muhtemelen. Yine de denemeye değer, teşekkürler :D
 
Durum
Üzgünüz bu konu cevaplar için kapatılmıştır...

Hala Discord sunucumuza katılmadın mı?

Büyük bir topluluğun parçası ol, etkinliklere katıl ve özel hediyeler kazanma şansı yakala!

Şimdi Katıl
Üst