Sol Reklam

Rehber Paket Dinleme ve Gönderme Dersleri | ProtocolLib Kullanımı | Bölüm 1

Durum
Mesaj gönderimine kapalı.

why_shiro

Aerospace Nerd
Emekli

Discord:

why_shiro

Katılım
3 Haziran 2019
Mesajlar
235
Elmaslar
252
Puanlar
7.295
Yaş
22
Yer
Türkiye
Discord İzni
Minecraft
GrimOK
Twitter
whyshiro_
bannermctr.png



Bölüm 1 : Paketler
(Haha uzun bir konu daha... Eski konularım silinmiş. En kısa zamanda tekrardan kaynak olarak paylaşacağım)

Minecraft sunucusu sürekli olarak client - server veya server - client şeklinde paketler göndermektedir. Paked nedir? Bu sorunun cevabı ise çok basittir. Sunucu ile oyuncu veya oyuncu ile sunucu arasında olan ve bir dizi bilgi taşıyan veriye paket denir. Paketler Minecraft'ta TCP (Transmission Control Protocol) üzerinden taşınır. Paketleri okumamız veya paket oluşturmak için elimizde iki farklı yo bulunmakta. Bunlardan bir tanesi ProtocolLib kullanmak, diğeri ise NMS (net.minecraft.server) kullanmaktır. NMS, ProtocolLib'e göre daha güçlü bir silah olmakla birlikte getirdiği bir silah ne kadar güçlü olursa geri tepmesi de o kadar güçlüdür mantığıyla kullanımı da o kadar tehlikelidir.

NMS kullanırken bizzat Minecraft sunucu mekaniğinin kalbine iniyoruz. NMS'ler import edildiği zaman kullanılan metoda göre import edildiği sürümü de import ederler. Bu demek ki eğer siz bir eklentiyi 1.15 sürümü için yazmışsanız ve eğer Reflection yapmamışsanız o eklentiyi diğer sürümler için kullanamazsınız demektir.
ProtocolLib ise cross-version olduğu için kullanımı kolaydır. Ancak yine de 1.18'deki bir paketi 1.8'e gönderemeyeceğimiz için hangi paketleri nerede kullanmamız gerektiğini bilmeliyiz.

Protokol ile ilgili detaylı bilgi almak için https://wiki.vg/Protocol websitesinden yararlanabilirsiniz. İlk baktığınızda gördüğünüz şeyler kafanızı karıştırabilir. Ancak bu derste olabildiğince wiki sayfasının nasıl okunduğunu anlaymaya çalışacağım.

Bölüm 2 : Paket Türleri
Minecraft protokolünde 4 adet paket türü bulunmaktadır. Bunlar:

  1. Handshaking​
  2. Status​
  3. Login​
  4. Play​
olmak üzere 4'e ayrılmıştır.

Handeshaking, sunucu ile oyuncu arasında giriş (login) işlemini başlatmadan önce yaptığı anlaşma şeklinde nitelendirilebilir. Handshaking evresinde gönderilen paket sunucu ile client arasında gönderilen ilk pakettir. Eğer handshake işlemi başarılı bir şekilde gerçekleşirse sunucu login işlemine başlar.

Login işlemi sunucunun oyuncu bilgilerini aldığı ve şifreleme işlemlerini başlattığı evredir. Bu evre başarılı bir şekilde tamamlanırsa oyuncu sunucuya giriş yapabilir. Özet olarak login evresi şu şekilde gösterilebilir:

  1. C→S: El Sıkışma (oturum açma)
  2. C→S: Giriş Başlangıcı
  3. S→C: Şifreleme İsteği
    Oyuncu kimlik doğrulaması
  4. C→S: Şifreleme Yanıtı
  5. Sunucu yetkilendirmesi, her ikisi de şifrelemeyi etkinleştirir
  6. S→C: Sıkıştırmayı Ayarla (isteğe bağlı)
  7. S→C: Giriş Başarılı

Play, oyuncunun sunucu içerisinde yaptığı tüm işlemleri ele alır. Sunucu ile oyuncu arasındaki paketler veya oyuncu ile sunucu arasında paketler bu paket türü altında ele alınır. Örneğin:

1- Oyuncunun hareket etmesi (Client -> Server)

2- Sunucuda olan bir patlamanın oyuncuya iletilmesi (Server -> Client)

Gördüğünüz üzere iki farklı karşılıkları olan paketler bulunmakta. Bunlar "serverbound" ve "clientbound" olmak üzere ikiye ayrılmaktadır.

Serverbound, client tarafından gelen paketleri receive eder (alır). Clientbound ise sunucudan gönderilen paketleri dinler. Yani bu kavramları şu şekilde daha iyi aklımızda tutabiliriz:
Gönderilen paketleri dinleme : Server -> Client (Sending)
Gelen paketleri dinleme : Client -> Server (Receiving)
Paketimizin genel olarak nerden nereye gönderildiğini de anladığımıza göre artık kodlama kısmına geçiş yapabiliriz.


Bölüm 3 : Kodlama
Birinci Bölüm

ProtocolLib'i kullanmak için eklentinin Spigot sayfasına giriş yapıyoruz.. Eklentiyi bu dersimizde Maven ile ekleyeceğiz. Sayfanın en alt kısmındaki iki adet kodu "pom.xml" dosyamıza yerleştiriyoruz :

Repository
HTML:
<repositories>
  <repository>
    <id>dmulloy2-repo</id>
    <url>https://repo.dmulloy2.net/repository/public/</url>
  </repository>
  <!-- Diger kutupaneler -->
</repositories>

Dependencies
HTML:
<dependencies>
  <dependency>
    <groupId>com.comphenix.protocol</groupId>
    <artifactId>ProtocolLib</artifactId>
    <version>4.7.0</version>
  </dependency>
  <!-- And so on -->
</dependencies>

Eğer eklentinizi Maven ile compile ediyorsanız "import" ettiğimiz "dependency"'ye <scope>provided</scope> eklememiz gerekmektedir. Aksi taktirde "shade" edilmeyip problem yaratabilir.

Bu kısmı tamamladıktan sonra "plugin.yml" dosyamıza gelip depend : [ProtocolLib] satırını ekliyoruz. Aksi taktirde eklentimiz ProtocolLib'e bağlı olarak gözükmeyeceği için düzgün bir şekilde başlatılamayacaktır.

İkinci Bölüm

Diğer gereken adımları tamamladıktan sonra artık ProtocolLib kullanarak paketleri dinlemeyi öğrenebiliriz. ProtocolLib'i kullanmamız için ilk önce "ProtocolManager" adında bir obje oluşturmamız gerekmektedir (onEnable metoduna):

Java:
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();

Ardından protocolManager objemizi kullanarak sanki bir event listen ediyormuş gibi paket inceliyoruz. Bunu yapmak için de protocolManager.addPacketListener() metodunu kullanıyoruz. Bu metod bizden bir adet packet listener istemektedir. Ancak eğer packet listerner interface'ini eklersek şöyle bir sonuçla karşı karşıya kalacağız:

Java:
        protocolManager.addPacketListener(new PacketListener() {
            @Override
            public void onPacketSending(PacketEvent packetEvent) {
              
            }

            @Override
            public void onPacketReceiving(PacketEvent packetEvent) {

            }

            @Override
            public ListeningWhitelist getSendingWhitelist() {
                return null;
            }

            @Override
            public ListeningWhitelist getReceivingWhitelist() {
                return null;
            }

            @Override
            public org.bukkit.plugin.Plugin getPlugin() {
                return null;
            }
        });

Bu bizim istemediğimiz bir sonuç. Çünkü biz sadece şu anda sunucunun receive ettiği paketlerle ilgileneceğiz. Yani sadece public void onPacketReceiving(PacketEvent packetEvent) {} metodunu override edeceğiz. Bunun için de PacketListener yerine kullanacağımız PacketAdapter metodunu kullanacağız. Yeni bir PacketAdapter oluşturuktan sonra karşımıza çıkan menüden public void onPacketReceiving(PacketEvent packetEvent) {}
metodunu override edeceğiz:

1651622968322.png

Bunu yaptıktan sonraki sonuç aşağıdaki gibi olacaktır:

Java:
        protocolManager.addPacketListener(new PacketAdapter() {

            @Override

            public void onPacketReceiving(PacketEvent event) {


            }

        });

Ancak hala eksiğimiz olmakta. PacketAdapter'in içine girmemiz gereken değerler bulunmakta. Bunlar sırası ile şu şekildedir:
  1. Instance​
  2. ListenerPriority​
  3. PacketType​
Şu anda kodumuzu ana Class'ımızda yazdığımız için ilk değere "this" yazabiliriz. Diğer değerimiz ise paketin önem sırasını temsil etmekte. Eğer önem daha düşükse, önem sırası daha yüksek olan bir paketten daha önce gönderilecektir. Bizim yazacağımız kodda önemi düşük olduğu için ListenerPriority.LOW yazıyorum. Son olarak paket türümüzü seçiyoruz. Ben oyununun sunucuya gönderdiği PacketType.Play.Client.POSITION paketini kullanıyorum. Bu paket sayesinde sunucu, oyuncunun hareketlerinden haberdar olabilir. Şimdi wikimize göz atalım ve oyuncunun konumundan nelere erişebildiğimize bakalım:

1651623559732.png


Bizim burada ilgileneceğimiz kısım FieldName ve FieldType. Bu paket 4 adet değer taşıyor, 3'ü "double" ve biri "boolean" olmak üzere. Bu değerlerin bir konteynırda saklandığını düşünün. Biz bu konteynırdan değerleri çekmeliyiz. Bu yüzden bir PacketContainer objesi oluşturuyorum:

Java:
        protocolManager.addPacketListener(new PacketAdapter(this, ListenerPriority.LOW, PacketType.Play.Client.POSITION) {

            @Override

            public void onPacketReceiving(PacketEvent event) {
                PacketContainer container = event.getPacket();

            }

        });

Artık konteynırımızdan değerleri okuyabiliriz. Her bir "field" değiken türünün bir "indexi"'i bulunmakta. Örneğin yukarıdan aşağı doğru giderken "double" için değerler [0,1,2] şeklinde artmakta. Ancak en alttaki "field"'i okurkan türünün değişmiş olduğunu göze alarak indeximizi tekrar 0 olarak kabul ediyoruz. Kodda daha iyi şekilde anlaşılacaktır:

Java:
        protocolManager.addPacketListener(new PacketAdapter(this, ListenerPriority.LOW, PacketType.Play.Client.POSITION) {

            @Override

            public void onPacketReceiving(PacketEvent event) {
                PacketContainer container = event.getPacket();

                double x = container.getDoubles().read(0);

                double y= container.getDoubles().read(1);

                double z = container.getDoubles().read(2);
              
                boolean isGround = container.getBooleans().read(0);
            }

        });

Farklı bir paket üzerinden indexleri üstüne yazarak verilen örnek:

1651624103161.png


container.getDoubles().read((int) index); ile paketin içindeki "double" değerini okuyabiliyoruz. Eğer paketten bir integer değeri almak isteseydik aynı kullandığımız gibi container.getIntegers().read((int) index); kodunu yazacaktık. Son olarak aldığımız değerleri oyuncuya göndermek için Player objesi oluşturuyoruz ve kodu tamamlıyoruz:

Java:
        protocolManager.addPacketListener(new PacketAdapter(this, ListenerPriority.LOW, PacketType.Play.Client.POSITION) {

            @Override

            public void onPacketReceiving(PacketEvent event) {
                PacketContainer container = event.getPacket();

                double x = container.getDoubles().read(0);

                double y= container.getDoubles().read(1);

                double z = container.getDoubles().read(2);

                boolean isGround = container.getBooleans().read(0);

                Player player = event.getPlayer();

                player.sendMessage("SUNUCUYA GÖNDERİLEN PAKETLER: " + "X: " + x + "Y: " + y + "Z: " + z);

                player.sendMessage("OYUNCU UÇUYOR MU: " + isGround);
            }

        });

Sonuç olarak oyuncunun hareketleri oyuncuya gönderilecektir. Şimdi ise sunucunun oyuncuya gönderdiği paketleri inceleyelim. Üstekki kodda override ettiğimiz kısmı public void onPacketSending(PacketEvent event) {} şeklinde değiştiriyoruz. Elde ettiğimiz sonuç şu şekildedir:

Java:
        protocolManager.addPacketListener(new PacketAdapter() {
            @Override
            public void onPacketSending(PacketEvent event) {

            }
        });

Yine wiki'den almış olduğun EntityPosition paketine göz atalım bu paket sunucuda algılanan tüm hareketleri oyuncuya gönderir, yani clientbound'dur. PacketAdapter'imizin içine gereken değerleri giriyoruz: this,ListenerPriority.LOW,PacketType.Play.Server.REL_ENTITY_MOVE

Eğer fark ettiyseniz PacketType.Play.Client yerine artık PacketType.Play.Server yazdık. Çünkü ele aldığımız paket sunucunun bize gönderdiği pakettir.

Wiki'den alabileceğimiz değerleri kontrol ediyoruz:

1651624694912.png


Bu sefer alacağımız değerler short olacak. Yani container.getShorts().read(index) şeklinde olacak. Gereken değerleri alıp oyuncuya gönderiyoruz:

Java:
        //Gönderilen paketleri dinleme : Serverbound -> Clientbound
        protocolManager.addPacketListener(new PacketAdapter(this,ListenerPriority.LOW,PacketType.Play.Server.REL_ENTITY_MOVE) {
            @Override
            public void onPacketSending(PacketEvent event) {
                PacketContainer container = event.getPacket();

                short x = container.getShorts().read(0);
                short y = container.getShorts().read(0);
                short z = container.getShorts().read(0);

                Player player = event.getPlayer();
                player.sendMessage("ENTITY ALGILANDI: " + "X: " + x + "Y: " + y + "Z: " + z);

            }
        });

Artık sunucuda hareket eden Entity'lerin koordinatlarını görebiliyoruz (sadece oyuncuya gönderilen paketler dahilindeki). Ek olarak Entity'leri ProtocolManager sayesinde bulabiliriz:

Java:
int entityID = container.getIntegers().read(0);
              
Entity entit = protocolManager.getEntityFromID(player.getWorld(),entityID);

Ve sonrasında bu entity üzerinde bu event çağırıldığı zaman işlemler yapabiliriz.


Bugün paketleri ve ProtocolLib ile gönderilen ve gelen paketleri okumayı öğrendik. Diğer bölümde ProtocolLib ile paketleri iptal etmeyi ve paket oluşturmayı öğreteceğim. Eğer aklınızda soru işareti varsa Discord sunucumuzdan bana dilediğiniz gibi soru sorabilirsiniz. Herkese iyi forumlar <3

joinDiscord.png
 
Son düzenleme:

Laving

Demir Cevheri Gibiyim

Discord:

laving.

Katılım
22 Aralık 2020
Mesajlar
178
Elmaslar
55
Puanlar
4.170
Yaş
20
Minecraft
xLaving
Çok güzel bir konu olmuş, eline sağlık dostum.
 

GameOfMineTR

Birisi mi Spawnlandı?
Katılım
24 Ağustos 2015
Mesajlar
1
Elmaslar
1
Puanlar
13.200
Yaş
25
Hiçbir şey anlamıyorum ama güzel emek var ?
 

why_shiro

Aerospace Nerd
Emekli

Discord:

why_shiro

Katılım
3 Haziran 2019
Mesajlar
235
Elmaslar
252
Puanlar
7.295
Yaş
22
Yer
Türkiye
Discord İzni
Minecraft
GrimOK
Twitter
whyshiro_
Durum
Mesaj gönderimine kapalı.
Neden altınlarını Discord sunucumuzda kazmıyorsun? TIKLA VE KATIL!
Yukarı