Yardım Android & İOS için Server Files

  • Konuyu açan Konuyu açan maxbro
  • Açılış Tarihi Açılış Tarihi
  • Yanıt Yanıt 32
  • Gösterim Gösterim 889
Konu sahibi bu konuda soru soruyor. Sorusu ile ilgili bilgisi olanların yanıtlamasını bekliyor.

maxbro

[GM]MaxBro
MT Üye
Mesaj
616
Çözümler
21
Beğeni
503
Puan
829
Ticaret Puanı
0
Merhaba arkadaşlar hepinize iyi günler iyi çalışmalar dilerim. Aklımda birkaç soru var size sormak istedim. Halihazırda var olan herhangi bir Server Files'i mobil platforma taşınma gibi bir çalışma yapan arkadaşlar var mı? Güzel eğlenceli bir files yapma düşüncesi içerisindeyim. Mobil platformlarda aktif olan sunucular mevcut. Bir şekilde yoluna devam ediyor. Bu alanda çeşitliliğin arması daha kaliteli içeriklerin devamını getireceğine inanıyorum. Mobil sürümü için hangi sunucu türü kullanılıyor. Güncellemeler nasıl yapılıyor , veri tabanı için ne kullanılıyor. Quest düzenlemeleri nasıl oluyor. Ödeme sistemi entegrasyonu nasıl çalışıyor. Bu konuda hizmet veren arkadaşlar var mı, varsa mobile taşıma maliyeti tahmini ne kadar olur. Bu konuda biraz bilgiye ihtiyacım var. Şimdiden teşekkürler.
 
Şuan sanırım joyistik döşe meshleri texture aktar felan yöntemiyle yapmışlar gibi görünüyor. Arka planını hiç görmedim. Kendi denediklerime göre yorum yapıyorum. Server yapısına dokunmamalarının sebebi packet yapısı olabilir. Her ne kadar eski bir oyun olsada paket akışı benim fikrimce çok güzel. Sıfırdan belki daha iyisi yapılabilir. Ama hangi ekip oturur yapar bilinmez. Royale2 duydum sanki aynı oyun olmayabilir. Ancak inceleyecem.

Royale Online ve Royale 2 aynı oyun. Oyundaki bazı zemin texture ları hava rengini falan değiştirdiler. Eskiden orjinalinde olduğu gibiydi. Belki de telif karşıtı bazı hareketlerdir. Ya da böylesi daha çok hoşlarına gitmiştir bilmiyorum artık.
 
Son düzenleme:
An itibariyle projeye başlamış bulunmaktayım. Aslında baya ilerlemişti proje, dün kayıt etmeyi unuttuğum için sıfırdan başlıyorum tekrar :D

Aslında buraya yazma sebebim şans eseri bulduğum .fbx dosyalarını burada paylaşmak. Savaşçı, ninja, sura, şaman ve lycan olmak üzere tüm karakterlerin birçok varyasyonlu dosyası, hareket animasyonları ve skill animasyonları olmak üzere birçok kaynak bulunmakta. Bu tarz bir proje düşünen arkadaşlara (şayet ellerinde yoksa) çok yardımcı olacağını düşünüyorum. Eğer elinde dönüştürülmüş map ve mob dosyaları olupta paylaşmak isteyen olursa, zamandan tasarruf etmiş oluruz.

5 karakter için .fbx uzantılı dosyaların
1769676793103.webp

1769676820068.webp
 
Güncelleme; Metin2 Database Manager adında bir script oluşturuldu. Bu script, item_proto ve item_names dosyalarından item bilgilerini çekip, unity'nin daha rahat kullanabileceği ve yedekleme işlemleri yapabileceği bir json dosyasına dönüştürüyor. Yeni item ekleme kısmıyla şuanlık ilgilenmedim. Projenin ilerleyen zamanlarında belki bununla ilgili birşeyler yapılır.


Her türlü fikir ve öneri benim için çok önemli.

1769692051423.webp
 
Ben yapabilirim sıfırdan yazabilirim flutterle forum sitem içinde baştan aşşa bir uygulama geliştirdim ama metin2 içinse sacmalık derim onun için yapıcagına daha basit daha iyi oyunlar var benzer şekilde yazarsın sadece metin2 server tarafını dokunmayız geri kalan kısmını baştan sona degiştiricez
 
burda bir örnegi yakın zamanda obj desteki eklenmisitm metin2 ye sorunları vardı cözmüştüm forumda konularım vardı metin2 işini bıraktım uygulama eklenti wep sitesi geliştiriyorum şuan sitem büyüdügü zaman inş cok yakında büyücek kapsamlı çalışmalar yaptım paylaşmadıgım eklentilerim var kendime özel yazdıgım arama motorları tarafında cok kapsamlı analiz ettim ve tasardım inş cok yakında bom gibi sitem geliyor ozaman baştan sona yeni bir oyun yazızam mobil pc tarzında olcak calışmalar yapıyorsan yaz bana desteklerim
 
Güncelleme; Her karakter için (toplamda 8 adet) animasyon kontrolcüsü oluşturuldu. Aynı zamanda shapeIndex ile ilgili karakterin zırhı ile beraber animasyonları da yükleniyor. bilgiler .msm dosyalarından çekiliyor. Bu sayede envanterde zırh değişimi yapılırken olası bir animasyon hatasının önüne geçilmiş olup, ve de kullanılacak dönüşüm küresi için de temel atılmış oldu.
1769767653739.webp

Fakat henüz .msm deki gibi zırh modelinin üstüne doku giydirme işlemini yapamadım.
 
Güncelleme; .msm deki zırh modellerinin görüntüleme ve doku giydirme eklendi. Bu sayede herhangi bir .msm dosyası doğrudan unity ortamına aktarılabilecek.

giphy.gif
 
Güncelleme; Metin2 orjinal klasördeki haritaları import eden script yazıldı. Bu sadeye sadece haritanın tam adını girerek otomatik bir şekilde Unity ortamına aktarma sağlanabiliyor. Yerleşim dokularını otomatik yerleştiriyor. Tabi ki doku texture dosyaları, dds'den png'ye çevrilip projeye eklenmelidir.



1770227415722.webp
 
Güncelleme; Metin2 orjinal klasördeki haritaları import eden script yazıldı. Bu sadeye sadece haritanın tam adını girerek otomatik bir şekilde Unity ortamına aktarma sağlanabiliyor. Yerleşim dokularını otomatik yerleştiriyor. Tabi ki doku texture dosyaları, dds'den png'ye çevrilip projeye eklenmelidir.



28969 eklentisini görüntüle
eklenti ile dds okumasınıda sağlayabilirsiniz, aşağıda örnek bırakıyorum ai yardımı ile basitçe toparlayıp kullanabilirsiniz.

Kod:
Genişlet Daralt Kopyala
// =============================================================================
// DDS Parsing and Decoding
// =============================================================================

#[derive(Copy, Clone)]
enum DdsFormat {
    Dxt1,
    Dxt3,
    Dxt5,
    Rgba8888,
}

#[derive(Copy, Clone)]
struct DdsInfo {
    width: i32,
    height: i32,
    mip_count: i32,
    format: DdsFormat,
    data_offset: usize,
    rgba_masks: (u32, u32, u32, u32),
    pitch: usize,
}

fn read_u32_le(data: &[u8], offset: usize) -> Option<u32> {
    if offset + 4 > data.len() {
        return None;
    }
    Some(u32::from_le_bytes([
        data[offset],
        data[offset + 1],
        data[offset + 2],
        data[offset + 3],
    ]))
}

fn parse_dds(data: &[u8]) -> Option<DdsInfo> {
    if data.len() < 128 {
        return None;
    }
    if &data[0..4] != b"DDS " {
        return None;
    }
    let height = read_u32_le(data, 12)? as i32;
    let width = read_u32_le(data, 16)? as i32;
    let mip_count = read_u32_le(data, 28)? as i32;
    let ddspf_offset = 76;
    let ddspf_flags = read_u32_le(data, ddspf_offset + 4)?;
    let fourcc = read_u32_le(data, ddspf_offset + 8)?;
    let rgb_bits = read_u32_le(data, ddspf_offset + 12)?;
    let r_mask = read_u32_le(data, ddspf_offset + 16)?;
    let g_mask = read_u32_le(data, ddspf_offset + 20)?;
    let b_mask = read_u32_le(data, ddspf_offset + 24)?;
    let a_mask = read_u32_le(data, ddspf_offset + 28)?;

    let flags = read_u32_le(data, 8)?;
    let pitch_or_linear = read_u32_le(data, 20)? as usize;

    let (format, is_dx10) = match fourcc {
        0x31545844 => (DdsFormat::Dxt1, false), // "DXT1"
        0x33545844 => (DdsFormat::Dxt3, false), // "DXT3"
        0x35545844 => (DdsFormat::Dxt5, false), // "DXT5"
        0x30315844 => {
            // "DX10" - extended header, need to read DXGI format
            if data.len() < 148 {
                return None;
            }
            let dxgi_format = read_u32_le(data, 128)?;
            let fmt = match dxgi_format {
                71 => DdsFormat::Dxt1,  // DXGI_FORMAT_BC1_UNORM
                72 => DdsFormat::Dxt1,  // DXGI_FORMAT_BC1_UNORM_SRGB
                74 => DdsFormat::Dxt3,  // DXGI_FORMAT_BC2_UNORM
                75 => DdsFormat::Dxt3,  // DXGI_FORMAT_BC2_UNORM_SRGB
                77 => DdsFormat::Dxt5,  // DXGI_FORMAT_BC3_UNORM
                78 => DdsFormat::Dxt5,  // DXGI_FORMAT_BC3_UNORM_SRGB
                _ => return None,
            };
            (fmt, true)
        }
        _ => {
            let has_rgb = (ddspf_flags & 0x40) != 0;
            if has_rgb && rgb_bits == 32 {
                (DdsFormat::Rgba8888, false)
            } else {
                return None;
            }
        }
    };

    let pitch = if matches!(format, DdsFormat::Rgba8888) && (flags & 0x8) != 0 && pitch_or_linear > 0 {
        pitch_or_linear
    } else {
        (width as usize) * 4
    };

    // DX10 header adds 20 bytes after the standard 128-byte header
    let data_offset = if is_dx10 { 148 } else { 128 };

    Some(DdsInfo {
        width,
        height,
        mip_count: if mip_count > 0 { mip_count } else { 1 },
        format,
        data_offset,
        rgba_masks: (r_mask, g_mask, b_mask, a_mask),
        pitch,
    })
}

fn mask_to_shift_and_bits(mask: u32) -> Option<(u32, u32)> {
    if mask == 0 {
        return None;
    }
    let shift = mask.trailing_zeros();
    let bits = mask.count_ones();
    Some((shift, bits))
}

fn expand_bits_to_u8(value: u32, bits: u32) -> u8 {
    if bits == 0 {
        return 0;
    }
    if bits >= 8 {
        return value as u8;
    }
    let max = (1u32 << bits) - 1;
    ((value * 255 + (max / 2)) / max) as u8
}

fn decode_dds_rgba(info: DdsInfo, data: &[u8], mip_index: i32) -> Option<(Vec<u8>, i32, i32)> {
    if mip_index < 0 {
        return None;
    }
    let mip = mip_index as usize;
    if mip >= info.mip_count as usize {
        return None;
    }

    let mut width = info.width as usize;
    let mut height = info.height as usize;
    let mut offset = info.data_offset;
    let block_size = match info.format {
        DdsFormat::Dxt1 => 8,
        DdsFormat::Dxt3 | DdsFormat::Dxt5 => 16,
        DdsFormat::Rgba8888 => 4,
    };

    let mut pitch = info.pitch;
    for _ in 0..mip {
        let size = match info.format {
            DdsFormat::Rgba8888 => pitch.saturating_mul(height),
            _ => {
                let blocks_w = (width.max(1) + 3) / 4;
                let blocks_h = (height.max(1) + 3) / 4;
                blocks_w * blocks_h * block_size
            }
        };
        offset = offset.saturating_add(size);
        width = (width / 2).max(1);
        height = (height / 2).max(1);
        pitch = width * 4;
    }

    let size = match info.format {
        DdsFormat::Rgba8888 => pitch.saturating_mul(height),
        _ => {
            let blocks_w = (width.max(1) + 3) / 4;
            let blocks_h = (height.max(1) + 3) / 4;
            blocks_w * blocks_h * block_size
        }
    };

    if offset + size > data.len() {
        return None;
    }
    let mip_data = &data[offset..offset + size];

    let decoded = match info.format {
        DdsFormat::Dxt1 => decode_texture(
            CodecTextureEncoding::Dxt1,
            mip_data,
            width as u32,
            height as u32,
        )
        .ok()?,
        DdsFormat::Dxt3 => decode_texture(
            CodecTextureEncoding::Dxt3,
            mip_data,
            width as u32,
            height as u32,
        )
        .ok()?,
        DdsFormat::Dxt5 => decode_texture(
            CodecTextureEncoding::Dxt5,
            mip_data,
            width as u32,
            height as u32,
        )
        .ok()?,
        DdsFormat::Rgba8888 => {
            let (r_mask, g_mask, b_mask, a_mask) = info.rgba_masks;
            let r_info = mask_to_shift_and_bits(r_mask);
            let g_info = mask_to_shift_and_bits(g_mask);
            let b_info = mask_to_shift_and_bits(b_mask);
            let a_info = mask_to_shift_and_bits(a_mask);
            let row_bytes = width * 4;
            let mut out = vec![0u8; width * height * 4];
            for y in 0..height {
                let src_row_start = y * pitch;
                let dst_row_start = y * row_bytes;
                for x in 0..width {
                    let src_offset = src_row_start + x * 4;
                    let dst_offset = dst_row_start + x * 4;
                    let value = u32::from_le_bytes([
                        mip_data[src_offset],
                        mip_data[src_offset + 1],
                        mip_data[src_offset + 2],
                        mip_data[src_offset + 3],
                    ]);
                    let r = r_info
                        .map(|(shift, bits)| expand_bits_to_u8((value & r_mask) >> shift, bits))
                        .unwrap_or(0);
                    let g = g_info
                        .map(|(shift, bits)| expand_bits_to_u8((value & g_mask) >> shift, bits))
                        .unwrap_or(0);
                    let b = b_info
                        .map(|(shift, bits)| expand_bits_to_u8((value & b_mask) >> shift, bits))
                        .unwrap_or(0);
                    let a = a_info
                        .map(|(shift, bits)| expand_bits_to_u8((value & a_mask) >> shift, bits))
                        .unwrap_or(255);
                    out[dst_offset] = r;
                    out[dst_offset + 1] = g;
                    out[dst_offset + 2] = b;
                    out[dst_offset + 3] = a;
                }
            }
            out
        }
    };

    Some((decoded, width as i32, height as i32))
}

#[no_mangle]
pub unsafe extern "C" fn GrannyRsGetDdsInfo(
    data: *const UInt8,
    data_len: Int32,
    out_width: *mut Int32,
    out_height: *mut Int32,
    out_mip_count: *mut Int32,
    out_format: *mut Int32,
) -> Bool32 {
    if data.is_null()
        || data_len <= 0
        || out_width.is_null()
        || out_height.is_null()
        || out_mip_count.is_null()
        || out_format.is_null()
    {
        return 0;
    }
    let slice = core::slice::from_raw_parts(data, data_len as usize);
    let info = match parse_dds(slice) {
        Some(info) => info,
        None => return 0,
    };
    *out_width = info.width;
    *out_height = info.height;
    *out_mip_count = info.mip_count;
    *out_format = match info.format {
        DdsFormat::Dxt1 => 1,
        DdsFormat::Dxt3 => 2,
        DdsFormat::Dxt5 => 3,
        DdsFormat::Rgba8888 => 4,
    };
    1
}

#[no_mangle]
pub unsafe extern "C" fn GrannyRsDecodeDdsRgba(
    data: *const UInt8,
    data_len: Int32,
    mip_index: Int32,
    out_pixels: *mut UInt8,
    out_len: Int32,
) -> Int32 {
    if data.is_null() || data_len <= 0 || out_pixels.is_null() || out_len <= 0 {
        return 0;
    }
    let slice = core::slice::from_raw_parts(data, data_len as usize);
    let info = match parse_dds(slice) {
        Some(info) => info,
        None => return 0,
    };
    let (decoded, _, _) = match decode_dds_rgba(info, slice, mip_index) {
        Some(result) => result,
        None => return 0,
    };
    if decoded.len() > out_len as usize {
        return 0;
    }
    core::ptr::copy_nonoverlapping(decoded.as_ptr(), out_pixels, decoded.len());
    decoded.len() as Int32
}

Kod:
Genişlet Daralt Kopyala
using System;
using System.IO;
using System.Runtime.InteropServices;
using UnityEditor;
using UnityEditor.AssetImporters;
using UnityEngine;

namespace GrannyRsUnity
{
    // Unity ships with a native DDS importer; avoid conflicting registration.
    [ScriptedImporter(1, "gr2dds")]
    public sealed class Gr2DdsImporter : ScriptedImporter
    {
        public bool linear = false;

        public override void OnImportAsset(AssetImportContext ctx)
        {
            string path = ResolveFileSystemPath(ctx.assetPath);
            byte[] data = File.ReadAllBytes(path);
            if (data == null || data.Length == 0)
            {
                Debug.LogWarning($"[Gr2DdsImporter] Empty DDS file: {ctx.assetPath}");
                return;
            }

            var dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
            try
            {
                IntPtr dataPtr = dataHandle.AddrOfPinnedObject();
                if (Native.GrannyRsGetDdsInfo(
                        dataPtr,
                        data.Length,
                        out int width,
                        out int height,
                        out int mipCount,
                        out int format) == 0)
                {
                    Debug.LogWarning($"[Gr2DdsImporter] Unsupported DDS format: {ctx.assetPath}");
                    return;
                }

                int pixelCount = width * height * 4;
                var pixels = new byte[pixelCount];
                var pixelsHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
                try
                {
                    int written = Native.GrannyRsDecodeDdsRgba(
                        dataPtr,
                        data.Length,
                        0,
                        pixelsHandle.AddrOfPinnedObject(),
                        pixels.Length);
                    if (written <= 0)
                    {
                        Debug.LogWarning($"[Gr2DdsImporter] Failed to decode DDS: {ctx.assetPath}");
                        return;
                    }
                }
                finally
                {
                    pixelsHandle.Free();
                }

                var texture = new Texture2D(width, height, TextureFormat.RGBA32, false, linear)
                {
                    name = Path.GetFileNameWithoutExtension(ctx.assetPath),
                };
                texture.SetPixelData(pixels, 0);
                texture.Apply(false, false);

                ctx.AddObjectToAsset("texture", texture);
                ctx.SetMainObject(texture);
            }
            finally
            {
                dataHandle.Free();
            }
        }

        private static string ResolveFileSystemPath(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                return path;
            }

            try
            {
                if (Path.IsPathRooted(path))
                {
                    return Path.GetFullPath(path);
                }
            }
            catch
            {
                // Ignore and fall through.
            }

            string normalized = path.Replace('\\', '/');
            if (normalized.StartsWith("Assets/", System.StringComparison.OrdinalIgnoreCase))
            {
                string relative = normalized.Substring("Assets/".Length);
                string combined = Path.Combine(Application.dataPath, relative);
                try
                {
                    return Path.GetFullPath(combined);
                }
                catch
                {
                    return combined;
                }
            }

            try
            {
                return Path.GetFullPath(path);
            }
            catch
            {
                return path;
            }
        }

        private static class Native
        {
            private const string DllName = "granny_rs";

            [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
            internal static extern int GrannyRsGetDdsInfo(
                IntPtr data,
                int dataLen,
                out int width,
                out int height,
                out int mipCount,
                out int format);

            [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
            internal static extern int GrannyRsDecodeDdsRgba(
                IntPtr data,
                int dataLen,
                int mipIndex,
                IntPtr outPixels,
                int outLen);
        }
    }
}
 
@maxbro elinize emeğinize sağlık hoş görünüyor her şey, aslında konuyu direkt geliştirici günlüğüne çevirseniz daha iyi olmaz mı?

Efektler ne durumda beceriler vs ? At madalyonu Profilii olan birisi 100k ya satıyormuş, oraya kadar gelirseniz basit bir scriptle tüm becerileri aktarmaniz için konuya eklerim. Tekrardan başarılar.
 
@maxbro elinize emeğinize sağlık hoş görünüyor her şey, aslında konuyu direkt geliştirici günlüğüne çevirseniz daha iyi olmaz mı?

Efektler ne durumda beceriler vs ? At madalyonu Profilii olan birisi 100k ya satıyormuş, oraya kadar gelirseniz basit bir scriptle tüm becerileri aktarmaniz için konuya eklerim. Tekrardan başarılar.
Adım adım ilerlemeye çalışıyorum. Beceriler hepsi animasyonlara çevrilmiş durumda. Şuan koşma düz vuruş gibi animasyonları sorunsuz gerçekleştiriyor. Efekt ekleme kısmına henüz gelemedim. Biraz karışık ilerliyorum aslında. Şuan objeleri otomatik eklemek için bir script yazmaya çalışacağım. Aslında amacım herşeyi otomatikleştirmeye yönelik. Çünkü ilerleyen zamanlarda proto tabloları, item list, msm dosyaları vesaire, normal bildiğimiz metin2 dosyalarını çok hızlı şekilde import edilen bir sistem yapmaya çalışıyorum. Bu durumda genel bir altyapı oluşmuş olur ve herhangi bir serverin yapısını unity ortamına çok hızlı bir şekilde aktarma işlemini yapabiliriz.
 
Güncelleme; SQL tabanlı veri çekme eklendi. Artık veriler tanımlanan ip adresindeki sql tablosundan çekiliyor. Aynı zamanda Karakterin job değerine göre animasyon ve model yüklemesi, item numarasına göre de zırh modelini msmdeki shape index ve item_proto tablosundaki verileri kullanarak karaktere giydiriyor.
Haritaları ve objeleri henüz tam anlamıyla halledebilmiş değilim.

1770403764151.webp
 
Geri
Üst