Client'e gif desteği ekleme

  • Konbuyu başlatan Konbuyu başlatan Admin
  • Başlangıç tarihi Başlangıç tarihi
  • Cevaplar Cevaplar 0
  • Görüntüleme Görüntüleme 57

Admin

Metin2Lobby
Yönetici
Founder
Katılım
6 Mayıs 2022
Mesajlar
52,647
Ticaret : 1 / 0 / 0
Bir serverın packinde tesadüfen gördüm deneme amaçlı yapmak istedim, ne amaçla kullanırsınız size kalmış. AniImage olarak onlarca resim kullanmak yerine ya da lonca simgeleri için işe yarar olabilir.



Kod:
CGraphicImage::~CGraphicImage() { }

Üstüne ekle:
Kod:
CGraphicImage::CGraphicImage(const char* c_szFileName, const void* c_pvBuf, int32_t iSize, uint32_t dwFilter) :     CResource(c_szFileName),     m_dwFilter(dwFilter) {     m_rect.bottom = m_rect.right = m_rect.top = m_rect.left = 0;     Load(iSize, c_pvBuf); }

* EterLib/GrpImage.h

Arat:
Kod:
CGraphicImage(const char* c_szFileName, uint32_t dwFilter = D3DX_FILTER_LINEAR);

Altına ekle:
Kod:
CGraphicImage(const char* c_szFileName, const void* c_pvBuf, int32_t iSize, uint32_t dwFilter = D3DX_FILTER_LINEAR);

* EterLib/Resource.cpp

Arat:
Kod:
void CResource::Reload()

Üstüne ekle:
Kod:
void CResource::Load(int32_t iSize, const void* c_pvBuf) {     if (me_state != STATE_EMPTY)         return;     if (OnLoad(iSize, c_pvBuf))         me_state = STATE_EXIST;     else         me_state = STATE_ERROR;     //Tracef("CResource::Load %s\n", GetFileName()); }

* EterLib/Resource.h
Arat:
Kod:
void            Load();

Altına ekle:
Kod:
void            Load(int32_t iSize, const void* c_pvBuf);

* EterPythonLib/PythonWindow.cpp

Ekle:
Kod:
#include <gif_lib.h> #include <wingdi.h>

--

Arat:
Kod:
void CAniImageBox::OnEndFrame()     {         PyCallClassMemberFunc(m_poHandler, "OnEndFrame", BuildEmptyTuple());     }     ///////////////////////////////////////////////////////////////////////////////////////////////     ///////////////////////////////////////////////////////////////////////////////////////////////     ///////////////////////////////////////////////////////////////////////////////////////////////

Altına ekle:
Kod:
uint32_t CGifImageBox::Type()     {         static uint32_t s_dwType = GetCRC32("CGifImageBox", strlen("CGifImageBox"));         return (s_dwType);     }     BOOL CGifImageBox::OnIsType(uint32_t dwType)     {         if (CGifImageBox::Type() == dwType)             return TRUE;         return FALSE;     }     CGifImageBox::CGifImageBox(PyObject* ppyObject)         : CAniImageBox(ppyObject)     {     }     CGifImageBox::~CGifImageBox()     {     }     bool RawFrameToBmpImage(SavedImage* currentFrame, GifFileType* gifFile, std::vector <uint8_t>& outData)     {         if (!currentFrame || !gifFile)         {             TraceError("RawFrameToBmpImage: Invalid parameters.");             return false;         }         auto& imageDesc = currentFrame->ImageDesc;         auto* colorMap = (imageDesc.ColorMap) ? imageDesc.ColorMap : gifFile->SColorMap;         if (!colorMap)         {             TraceError("RawFrameToBmpImage: ColorMap is null.");             return false;         }         const int width = imageDesc.Width;         const int height = imageDesc.Height;         const int rowPadding = (4 - (width * 3 % 4)) % 4;         const std::size_t rowDataSize = static_cast<std::size_t>(width * 3 + rowPadding) * height;         BITMAPFILEHEADER fileHeader = {             0x4D42, // "BM"             static_cast<uint32_t>(sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + rowDataSize),             0, // Reserved1             0, // Reserved2             sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)         };         BITMAPINFOHEADER infoHeader = {             sizeof(BITMAPINFOHEADER),             width,             height,             1, // Planes             24, // BitCount             0, // Compression             static_cast<uint32_t>(rowDataSize),             0, // XPelsPerMeter             0, // YPelsPerMeter             0, // ClrUsed             0  // ClrImportant         };         outData.resize(fileHeader.bfSize);         std::memcpy(outData.data(), &fileHeader, sizeof(fileHeader));         std::memcpy(outData.data() + sizeof(fileHeader), &infoHeader, sizeof(infoHeader));         auto pixelData = outData.begin() + fileHeader.bfOffBits;         for (int y = height - 1; y >= 0; --y)         {             for (int x = 0; x < width; ++x)             {                 const int index = y * width + x;                 const GifByteType colorIndex = currentFrame->RasterBits[index];                 const GifColorType& gifColor = colorMap->Colors[colorIndex];                 *pixelData++ = gifColor.Blue;                 *pixelData++ = gifColor.Green;                 *pixelData++ = gifColor.Red;             }             pixelData = std::fill_n(pixelData, rowPadding, 0x00);         }     }     bool CGifImageBox::LoadGif(const char* c_szFileName)     {         if (!c_szFileName || !*c_szFileName)             return false;         // Open GIF file         int error;         GifFileType* gif = DGifOpenFileName(c_szFileName, &error);         if (!gif)         {             const char* c_szError = GifErrorString(error);             TraceError("Failed to open GIF file: %s with error: %s", c_szFileName, c_szError);             return false;         }         // Read the GIF into memory         if (DGifSlurp(gif) == GIF_ERROR)         {             TraceError("Failed to read GIF file: %s", c_szFileName);             DGifCloseFile(gif, &error);             return false;         }         // Iterate through the frames         for (int i = 0; i < gif->ImageCount; ++i)         {             // Get the current frame             SavedImage* currentImage = &gif->SavedImages[i];             // Convert the frame to BMP image             std::vector <uint8_t> bmpBuffer;             if (!RawFrameToBmpImage(currentImage, gif, bmpBuffer))             {                 TraceError("Failed to convert GIF(%s) frame #%d to BMP image.", c_szFileName, i);                 continue;             }             // Create name for the frame             char frameFileName[256]{ '\0' };             snprintf(frameFileName, sizeof(frameFileName), "gif_frame%d_%s", i, c_szFileName);             // Create the image instance             CGraphicImage* pImage = new CGraphicImage(frameFileName, bmpBuffer.data(), bmpBuffer.size(), D3DX_FILTER_LINEAR);             if (!pImage)             {                 TraceError("Failed to create image instance for GIF(%s) frame #%d.", c_szFileName, i);                 continue;             }             // Create the expanded image instance             CGraphicExpandedImageInstance* pImageInstance = CGraphicExpandedImageInstance::New();             pImageInstance->SetImagePointer(pImage);             // Add the image instance to the vector if it's not empty             if (pImageInstance->IsEmpty())             {                 CGraphicExpandedImageInstance::Delete(pImageInstance);             }             else             {                 pImageInstance->SetPosition(m_rect.left, m_rect.top);                 m_ImageVector.push_back(pImageInstance);             }         }         // Clean up         DGifCloseFile(gif, &error);         return true;     }     bool CGifImageBox::UnloadGif()     {         for (auto pImage : m_ImageVector)             CGraphicExpandedImageInstance::Delete(pImage);         m_ImageVector.clear();         return true;     }     ///////////////////////////////////////////////////////////////////////////////////////////////     ///////////////////////////////////////////////////////////////////////////////////////////////     ///////////////////////////////////////////////////////////////////////////////////////////////

* EterPythonLib/PythonWindow.h

Arat:
Kod:
// Button     class CButton : public CWindow

Üstüne ekle:
Kod:
class CGifImageBox : public CAniImageBox     {     public:         static uint32_t Type();         public:             CGifImageBox(PyObject* ppyObject);             virtual ~CGifImageBox();             bool LoadGif(const char* c_szFileName);             bool UnloadGif();         protected:             BOOL OnIsType(uint32_t dwType);     };

* EterPythonLib\PythonWindowManager.cpp

Arat:
Kod:
case WT_ANI_IMAGEBOX:             return new CAniImageBox(po);             break;

Altına ekle:
Kod:
case WT_GIF_IMAGEBOX:             return new CGifImageBox(po);             break;

---

Arat:
Kod:
CWindow* CWindowManager::RegisterAniImageBox(PyObject* po, const char* c_szLayer)     {         assert(m_LayerWindowMap.end() != m_LayerWindowMap.find(c_szLayer));         CWindow* pWin = new CAniImageBox(po);         m_LayerWindowMap[c_szLayer]->AddChild(pWin); #ifdef __WINDOW_LEAK_CHECK__         gs_kSet_pkWnd.insert(pWin); #endif         return pWin;     }

Altına ekle:
Kod:
CWindow* CWindowManager::RegisterGifImageBox(PyObject* po, const char* c_szLayer)     {         assert(m_LayerWindowMap.end() != m_LayerWindowMap.find(c_szLayer));         CWindow* pWin = new CGifImageBox(po);         m_LayerWindowMap[c_szLayer]->AddChild(pWin); #ifdef __WINDOW_LEAK_CHECK__         gs_kSet_pkWnd.insert(pWin); #endif         return pWin;     }

* EterPythonLib\PythonWindowManager.h

Arat:
Kod:
WT_ANI_IMAGEBOX,

Altına ekle:
Kod:
WT_GIF_IMAGEBOX,

--

Arat:
Kod:
CWindow* RegisterImageBox(PyObject* po, const char* c_szLayer);

Altına ekle:
Kod:
CWindow* RegisterGifImageBox(PyObject* po, const char* c_szLayer);

* EterPythonLib\PythonWindowManagerModule.cpp

Ekle:
Kod:
PyObject* wndMgrRegisterGifImageBox(PyObject* poSelf, PyObject* poArgs) {     PyObject* po;     if (!PyTuple_GetObject(poArgs, 0, &po))         return Py_BuildException();     char* szLayer;     if (!PyTuple_GetString(poArgs, 1, &szLayer))         return Py_BuildException();     UI::CWindow* pWindow = UI::CWindowManager::Instance().RegisterGifImageBox(po, szLayer);     return Py_BuildValue("i", pWindow); } PyObject* wndGifImageLoadGif(PyObject* poSelf, PyObject* poArgs) {     UI::CWindow* pWindow;     if (!PyTuple_GetWindow(poArgs, 0, &pWindow))         return Py_BuildException();     char* szFileName;     if (!PyTuple_GetString(poArgs, 1, &szFileName))         return Py_BuildException();     ((UI::CGifImageBox*)pWindow)->LoadGif(szFileName);     return Py_BuildNone(); } PyObject* wndGifImageUnloadGif(PyObject* poSelf, PyObject* poArgs) {     UI::CWindow* pWindow;     if (!PyTuple_GetWindow(poArgs, 0, &pWindow))         return Py_BuildException();     ((UI::CGifImageBox*)pWindow)->UnloadGif();     return Py_BuildNone(); }

Arat:
Kod:
{ "RegisterAniImageBox",        wndMgrRegisterAniImageBox,            METH_VARARGS },

Altına ekle:
Kod:
{ "RegisterGifImageBox",        wndMgrRegisterGifImageBox,            METH_VARARGS },         { "LoadGif",                    wndGifImageLoadGif,                    METH_VARARGS },         { "UnloadGif",                    wndGifImageUnloadGif,                METH_VARARGS },

*ScriptLib\Resource.cpp

Arat:
Kod:
m_resManager.RegisterResourceNewFunctionPointer("bmp", NewImage);

Altına ekle:
Kod:
m_resManager.RegisterResourceNewFunctionPointer("gif", NewImage);

* UserInterface\UserInterface.cpp

Ekle:
Kod:
#pragma comment( lib, "gif.lib" )

---

* root/ui.py

Ekle:
Kod:
class GifBox(Window):     def __init__(self, layer = "UI"):         Window.__init__(self, layer)         self.eventDict={}         self.arg = -1         self.argOut = -1         self.imageLoaded = False     def __del__(self):         Window.__del__(self)     def RegisterWindow(self, layer):         self.hWnd = wndMgr.RegisterGifImageBox(self, layer)     def LoadImage(self, imageName):         self.name=imageName         wndMgr.LoadGif(self.hWnd, imageName)         self.imageLoaded = True         if len(self.eventDict)!=0:             print "LOAD IMAGE", self, self.eventDict     def UnloadImage(self):         if self.imageLoaded:             wndMgr.UnloadGif(self.hWnd)             self.imageLoaded = False     def SetAlpha(self, alpha):         wndMgr.SetDiffuseColor(self.hWnd, 1.0, 1.0, 1.0, alpha)     def SetDiffuseColor(self, r,g,b,a=1.0):         wndMgr.SetDiffuseColor(self.hWnd, r,g,b,a)     def SetAlphaColor(self, r,g,b,alpha=1.0):         wndMgr.SetDiffuseColor(self.hWnd, r,g,b, alpha)     def GetWidth(self):         return wndMgr.GetWidth(self.hWnd)     def GetHeight(self):         return wndMgr.GetHeight(self.hWnd)        def SetRenderingRect(self, left, top, right, bottom):         wndMgr.SetRenderingRect(self.hWnd, left, top, right, bottom)     def SetPercentage(self, curValue, maxValue):         if maxValue:             self.SetRenderingRect(0.0, 0.0, -1.0 + float(curValue) / float(maxValue), 0.0)         else:             self.SetRenderingRect(0.0, 0.0, 0.0, 0.0)        def SetScale(self, xScale, yScale):         wndMgr.SetScale(self.hWnd, xScale, yScale)

---

Extern'e ekle:


---

Örnek:
Kod:
self.testgif = ui.GifBox()         self.testgif.SetParent(self)         self.testgif.SetPosition(180,20)         self.testgif.LoadImage("nice.gif")         self.testgif.Show()

---

BMP yerine TGA kullanmak için;

Client'e Gif Desteği Ekleme Rehberi

Metin2 Lobby üzerinden geliştirilen özel sunucularda görsel deneyimi zenginleştirmek için client tarafında GIF desteği eklemek son derece faydalıdır. Bu rehber sayesinde hem client tarafında hem de UI Script seviyesinde nasıl destek sağlayacağınızı adım adım öğrenebilirsiniz.

Neden GIF Desteği?
Metin2 özel sunucularında kullanıcı arayüzünü daha dinamik hale getirmek ve görsel olarak dikkat çekici efektler eklemek için GIF formatı idealdir. Özellikle PvP sistemlerinde özel animasyonlarla geri sayım efektleri, ödül sistemleri veya özel eventler için kullanılır. Bu destek sayesinde oyuncuların dikkatini çekecek görseller entegre edilebilir.

UYARI: Bu işlem için client kaynak koduna (client src) erişim gereklidir. Ayrıca Python GUI (Py Root) ve UIScript bilgisi önemlidir.

Adım 1: GIF Decoder Entegrasyonu
Öncelikle Python tarafında PIL (Pillow) kütüphanesini yüklemeniz gerekir. Bu kütüphane sayesinde GIF dosyalarının frame'lerini okuyabilirsiniz.
pip install Pillow

Adım 2: UI Script Dosyasına Uygulama
UIScript dosyalarında bir imageBox nesnesi tanımlanmalı. Bu nesne, GIF dosyasının gösterileceği alandır. Daha sonra bir Timer yardımıyla frame'ler sırayla değiştirilir.

Adım 3: Py Root Tarafında Frame Döngüsü
Py Root üzerinde bir sınıf oluşturulur. Bu sınıf GIF dosyasının her frame'ini sırasıyla yükler ve imageBox nesnesine atar. Bu işlem bir timer ile kontrol edilir.

Avantajları
GIF desteği sayesinde:
- Oyuncu arayüzü daha interaktif olur.
- Özel efektlerle PvP sistemlerinde geri sayım veya özel animasyonlar sağlanabilir.
- Event zamanlayıcıları daha görsel hale gelir.
- Sunucu çekiciliği artar.

Kaynak Kod Örneği
from PIL import Image
import ui
class AnimatedGif(ui.Window):
def __init__(self):
super(AnimatedGif, self).__init__()
self.image = None
self.gif = Image.open('path/to/gif.gif')
self.timer = ui.Timer(self.update_frame)
def update_frame(self):
try:
self.gif.seek(self.gif.tell() + 1)
self.image.LoadImage(self.gif)
except EOFError:
self.gif.seek(0)
pass

Not: Bu örnek temel bir yapıdır. Gerçek uygulamada Martysama gibi frameworklerle entegre çalışmak için daha fazla adaptasyon gerekir.

Sonuç
Metin2 özel sunucularında client tarafında GIF desteği eklemek, görsel deneyimi ciddi anlamda artırır. Doğru entegrasyon ile PvP sistemlerinde, event sistemlerinde ve özel efektlerde kullanabilirsiniz. Py GUI, UIScript ve client src bilginizi bir araya getirerek bu özelliği kolayca entegre edebilirsiniz.


Adding GIF Support to Client

Metin2 Lobby enables enriching visual experience on private servers by adding GIF support to the client side. This guide will help you learn how to implement support at both the client and UI Script levels step by step.

Why Add GIF Support?
GIF formats are ideal for making user interfaces more dynamic and visually appealing in Metin2 private servers. Especially in PvP systems, special animations can be used for countdown effects, reward systems, or events. With this support, eye-catching visuals can be integrated into gameplay.

WARNING: Access to client source code (client src) is required for this process. Knowledge of Python GUI (Py Root) and UIScript is also essential.

Step 1: Integrate GIF Decoder
Firstly, install the PIL (Pillow) library in Python. This library allows you to read GIF frames.
pip install Pillow

Step 2: Apply to UI Script File
An imageBox object must be defined in UIScript files. This object serves as the display area for the GIF. Then, frames are updated sequentially using a Timer.

Step 3: Frame Loop in Py Root
A class is created in Py Root that loads each GIF frame sequentially and assigns it to the imageBox object. This process is controlled via a timer.

Benefits[/BR][/BR]With GIF support:
- Player interface becomes more interactive.
- Special animations can be applied in PvP systems such as countdowns or unique effects.
- Event timers become more visual.
- Server appeal increases.

Sample Code[/BR][/BR]from PIL import Image
import ui
class AnimatedGif(ui.Window):
def __init__(self):
super(AnimatedGif, self).__init__()
self.image = None
self.gif = Image.open('path/to/gif.gif')
self.timer = ui.Timer(self.update_frame)
def update_frame(self):
try:
self.gif.seek(self.gif.tell() + 1)
self.image.LoadImage(self.gif)
except EOFError:
self.gif.seek(0)
pass

Note: This example is basic. In real applications, further adaptation may be needed to work with frameworks like Martysama.

Conclusion
Adding GIF support to the client side in Metin2 private servers significantly enhances visual experience. Proper integration allows its use in PvP systems, event systems, and special effects. You can easily integrate this feature by combining your knowledge of Py GUI, UIScript, and client src.
 

Şuan Bu Konuyu Görüntüleyen Kullanıcılar (Toplam : 0, Üye : 0, Misafir : 0)

Benzer konular

Geri
Üst Alt