• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!
  • If you're using Gesior 2012 or MyAAC, please review this thread for information about a serious security vulnerability and a fix.

Feature Instance System

Itutorial

Excellent OT User
Joined
Dec 23, 2014
Messages
2,272
Solutions
68
Reaction score
907
This is my fix for my previous thread. Hopefully they will remove the old one.

This system is critical for any server that has systems like: dungeons, CTF, ect... This is an istance system that works exactly like WoW. I hope it gets put into TFS master branch because it is very useful.

Instead of having to make copies of a map 50x you can use this code to EASILY put players in different instances on the same map.

Code:
creature:getInstance()
creature:changeInstance(instanceId)

Game.createInstanceNpc(name, pos, instanceid[, extended = false[, force = false]])
Game.createInstanceMonster(name, pos, instanceid[, extended = false[, force = false]])

creature.h

under
C++:
void decrementReferenceCounter() {
add
C++:
void setInstanceID(uint32_t id) {
            instanceId = id;
        }

        uint32_t getInstanceID() const {
            return instanceId;
        }

under
C++:
uint8_t drunkenness = 0;
add
C++:
uint32_t instanceId = 1;

monster.cpp

find
C++:
bool Monster::isOpponent(const Creature* creature) const
add inside the method at the top
C++:
if (const Player* player = creature->getPlayer()) {
        if (getInstanceID() != player->getInstanceID()) {
            return false;
        }
    }

game.h

under
C++:
bool placeCreature(Creature* creature, const Position& pos, bool extendedPos = false, bool forced = false);
add
C++:
bool placeInstanceCreature(Creature* creature, const Position& pos, uint32_t instanceId = 0, bool extendedPos = false, bool forced = false);

under
C++:
static void removeCreatureCheck(Creature* creature);
add
C++:
bool changeInstance(Creature* creature, uint32_t instanceID);

game.cpp
under whole method
C++:
bool Game::placeCreature(Creature* creature, const Position& pos, bool extendedPos /*=false*/, bool forced /*= false*/)
add
C++:
bool Game::placeInstanceCreature(Creature* creature, const Position& pos, uint32_t instanceId /*0*/, bool extendedPos /*=false*/, bool forced /*= false*/)
{
    if (!internalPlaceCreature(creature, pos, extendedPos, forced)) {
        return false;
    }

    creature->setInstanceID(instanceId);

    SpectatorVec spectators;
    map.getSpectators(spectators, creature->getPosition(), true);
    for (Creature* spectator : spectators) {
        if (Player* tmpPlayer = spectator->getPlayer()) {
            if (tmpPlayer->canSeeCreature(creature)) {
                tmpPlayer->sendCreatureAppear(creature, creature->getPosition(), true);
            }
        }
    }

    for (Creature* spectator : spectators) {
        if (Player* tmpPlayer = spectator->getPlayer()) {
            if (tmpPlayer->canSeeCreature(creature)) {
                spectator->onCreatureAppear(creature, true);
            }
        }
    }

    creature->getParent()->postAddNotification(creature, nullptr, 0);

    addCreatureCheck(creature);
    creature->onPlacedCreature();
    return true;
}

bool Game::changeInstance(Creature* creature, uint32_t instanceID) {
    if (!creature || !instanceID) {
        return false;
    }

    // Set creatures new instance ID
    creature->setInstanceID(instanceID);

    // We need to remove the creature from any players screens and remove players from the creatures sceen if the creature is a player.
    SpectatorVec spectators;
    map.getSpectators(spectators, creature->getPosition(), true, false);
    Tile* tile = creature->getTile();
    const Position& tilePosition = tile->getPosition();

    for (Creature* spectator : spectators) {
        if (Player* player = spectator->getPlayer()) { // Spectator is a player
            if (spectator->getID() == creature->getID()) { // Spectator is the creature changing instance
                //Remove creatures not in new instance
                for (Creature* spectatorTwo : spectators) {
                    if (spectatorTwo->getInstanceID() != instanceID && spectatorTwo != spectator) { // Creature is not in same instance and isn't the player
                        player->sendRemoveTileCreature(spectatorTwo, tilePosition, tile->getClientIndexOfCreature(player, spectatorTwo));
                    }
                }
            } else { // Spectator is not the creature changing instance
                // Remove the creature from their client.
                player->sendRemoveTileCreature(creature, tilePosition, tile->getClientIndexOfCreature(player, creature));
            }
        }
    }

    // Populate creature on players screens in same instance
    for (Creature* spectator : spectators) {
        if (Player* player = spectator->getPlayer()) { // Spectator is a player
            if (spectator->getID() == creature->getID()) { // Spectator is the player changing instance
                // Populate his screen
                for (Creature* spectatorTwo : spectators) {
                    if (spectator != spectatorTwo) { // Creature is not the player add check if they are in the new instance
                        if (spectatorTwo->getInstanceID() == instanceID) { // Creature is in the new instance add it to players client
                            player->sendCreatureAppear(spectatorTwo, spectatorTwo->getPosition(), true);
                        }
                    }
                }
            } else { // Spectator is not the creature changing instance add creature if he is in the instance
                if (player->getInstanceID() == instanceID) { // Spectator is in the instance, lets add the creature to his client
                    player->sendCreatureAppear(creature, creature->getPosition(), true);
                }
            }
        }
    }

    for (Creature* summon : creature->summons) {
        changeInstance(summon, instanceID);
    }

    return true;
}

player.cpp
find
C++:
bool Player::canSeeCreature(const Creature* creature) const
at bottom of method add
C++:
if (creature->getInstanceID() != getInstanceID() || getInstanceID() != creature->getInstanceID()) {
        return false;
    }

luascript.h
under
C++:
static int luaGameCreateMonster(lua_State* L);
add
C++:
static int luaGameCreateInstanceMonster(lua_State* L);
under
C++:
static int luaGameCreateNpc(lua_State* L);
add
C++:
static int luaGameCreateInstanceNpc(lua_State* L);
under
C++:
static int luaCreatureCanSeeInvisibility(lua_State* L);
add
C++:
static int luaCreatureChangeInstance(lua_State* L);
static int luaCreatureGetInstance(lua_State* L);

luascript.cpp
under
C++:
registerMethod("Game", "createMonster", LuaScriptInterface::luaGameCreateMonster);
add
C++:
registerMethod("Game", "createInstanceMonster", LuaScriptInterface::luaGameCreateInstanceMonster);
under
C++:
registerMethod("Game", "createNpc", LuaScriptInterface::luaGameCreateNpc);
add
C++:
registerMethod("Game", "createInstanceNpc", LuaScriptInterface::luaGameCreateInstanceNpc);

under
C++:
registerMethod("Creature", "canSeeInvisibility", LuaScriptInterface::luaCreatureCanSeeInvisibility);
add
C++:
registerMethod("Creature", "changeInstance", LuaScriptInterface::luaCreatureChangeInstance);
registerMethod("Creature", "getInstance", LuaScriptInterface::luaCreatureGetInstance);
under method
C++:
int LuaScriptInterface::luaGameCreateUniqueMonster(lua_State* L)
add
C++:
int LuaScriptInterface::luaGameCreateInstanceMonster(lua_State* L)
{
    // Game.createInstanceMonster(monsterName, position[, instanceId[, extended = false[, force = false]]])
    Monster* monster = Monster::createMonster(getString(L, 1));
    if (!monster) {
        lua_pushnil(L);
        return 1;
    }

    const Position& position = getPosition(L, 2);
    uint32_t instanceId = getNumber<uint32_t>(L, 3, 0);
    bool extended = getBoolean(L, 4, false);
    bool force = getBoolean(L, 5, false);
    if (g_game.placeInstanceCreature(monster, position, instanceId, extended, force)) {
        pushUserdata<Monster>(L, monster);
        setMetatable(L, -1, "Npc");
    }
    else {
        delete monster;
        lua_pushnil(L);
    }
    return 1;
}

int LuaScriptInterface::luaGameCreateInstanceNpc(lua_State* L)
{
    // Game.createInstanceNpc(npcName, position[, instanceId[, extended = false[, force = false]]])
    Npc* npc = Npc::createNpc(getString(L, 1));
    if (!npc) {
        lua_pushnil(L);
        return 1;
    }

    const Position& position = getPosition(L, 2);
    uint32_t instanceId = getNumber<uint32_t>(L, 3, 0);
    bool extended = getBoolean(L, 4, false);
    bool force = getBoolean(L, 5, false);
    if (g_game.placeInstanceCreature(npc, position, instanceId, extended, force)) {
        pushUserdata<Npc>(L, npc);
        setMetatable(L, -1, "Npc");
    }
    else {
        delete npc;
        lua_pushnil(L);
    }
    return 1;
}

int LuaScriptInterface::luaCreatureGetInstance(lua_State* L)
{
    // creature:getInstance()
    Creature* creature = getUserdata<Creature>(L, 1);
    if (!creature) {
        lua_pushnil(L);
        return 1;
    }

    uint32_t instanceID = creature->getInstanceID();
    lua_pushnumber(L, instanceID);
    return 1;
}

int LuaScriptInterface::luaCreatureChangeInstance(lua_State* L)
{
    // creature:changeInstance(id)
    Creature* creature = getUserdata<Creature>(L, 1);
    if (!creature) {
        lua_pushnil(L);
        return 1;
    }

    uint32_t instanceID = getNumber<uint32_t>(L, 2);
    if (!instanceID) {
        lua_pushnil(L);
    }

    if (!g_game.changeInstance(creature, instanceID)) {
        lua_pushnil(L);
        return 1;
    }

    lua_pushboolean(L, true);
    return 1;
}

Those last functions should be moved to the correct place to keep it clean. You don't need to though. You can easily change creatures instances at anytime, anywhere. Hope someone enjoys.
 
Last edited:
OP
OP
Itutorial

Itutorial

Excellent OT User
Joined
Dec 23, 2014
Messages
2,272
Solutions
68
Reaction score
907
Go make it then man. How about contribute instead of pointing out problems?
Post automatically merged:

It does hide effects and chat, ect. idk about browse field and items but it wouldn't be hard to just add a canSee type thing for those also.
 

Shalaby

Time has made me wiser, but no more patient.
Joined
Feb 17, 2011
Messages
222
Solutions
5
Reaction score
65
Location
Egypt
This is my fix for my previous thread. Hopefully they will remove the old one.

This system is critical for any server that has systems like: dungeons, CTF, ect... This is an istance system that works exactly like WoW. I hope it gets put into TFS master branch because it is very useful.

Instead of having to make copies of a map 50x you can use this code to EASILY put players in different instances on the same map.

Code:
creature:getInstance()
creature:changeInstance(instanceId)

Game.createInstanceNpc(name, pos, instanceid[, extended = false[, force = false]])
Game.createInstanceMonster(name, pos, instanceid[, extended = false[, force = false]])

creature.h

under
C++:
void decrementReferenceCounter() {
add
C++:
void setInstanceID(uint32_t id) {
            instanceId = id;
        }

        uint32_t getInstanceID() const {
            return instanceId;
        }

under
C++:
uint8_t drunkenness = 0;
add
C++:
uint32_t instanceId = 1;

monster.cpp

find
C++:
bool Monster::isOpponent(const Creature* creature) const
[code]
add inside the method at the top
[code=cpp]
if (const Player* player = creature->getPlayer()) {
        if (getInstanceID() != player->getInstanceID()) {
            return false;
        }
    }

game.h

under
C++:
bool placeCreature(Creature* creature, const Position& pos, bool extendedPos = false, bool forced = false);
add
C++:
bool placeInstanceCreature(Creature* creature, const Position& pos, uint32_t instanceId = 0, bool extendedPos = false, bool forced = false);

under
C++:
static void removeCreatureCheck(Creature* creature);
add
C++:
bool changeInstance(Creature* creature, uint32_t instanceID);

game.cpp
under whole method
C++:
bool Game::placeUniqueCreature(Creature* creature, const Position& pos, std::list<uint32_t> playerlist, bool extendedPos /*=false*/, bool forced /*= false*/)
add
C++:
bool Game::placeInstanceCreature(Creature* creature, const Position& pos, uint32_t instanceId /*0*/, bool extendedPos /*=false*/, bool forced /*= false*/)
{
    if (!internalPlaceCreature(creature, pos, extendedPos, forced)) {
        return false;
    }

    creature->setInstanceID(instanceId);

    SpectatorVec spectators;
    map.getSpectators(spectators, creature->getPosition(), true);
    for (Creature* spectator : spectators) {
        if (Player* tmpPlayer = spectator->getPlayer()) {
            if (tmpPlayer->canSeeCreature(creature)) {
                tmpPlayer->sendCreatureAppear(creature, creature->getPosition(), true);
            }
        }
    }

    for (Creature* spectator : spectators) {
        if (Player* tmpPlayer = spectator->getPlayer()) {
            if (tmpPlayer->canSeeCreature(creature)) {
                spectator->onCreatureAppear(creature, true);
            }
        }
    }

    creature->getParent()->postAddNotification(creature, nullptr, 0);

    addCreatureCheck(creature);
    creature->onPlacedCreature();
    return true;
}

bool Game::changeInstance(Creature* creature, uint32_t instanceID) {
    if (!creature || !instanceID) {
        return false;
    }

    // We need to remove the creature from any players screens and remove players from the creatures sceen if the creature is a player.
    SpectatorVec spectators;
    map.getSpectators(spectators, creature->getPosition(), true, false, 0, 9, 0, 9);

    for (Creature* spectator : spectators) {
        if (Player* tmpPlayer = spectator->getPlayer()) {
            if (tmpPlayer->getID() == creature->getID()) {
                // Spectator is player and is the creature changing instance : We need to remove all creatures from his client
                std::vector<int32_t> oldStackPosVector;
                size_t i = 0;
                for (Creature* spectatorTwo : spectators) {
                    if (tmpPlayer->getID() != spectatorTwo->getID()) {
                        if (tmpPlayer->canSeeCreature(spectatorTwo)) {
                            // SpectatorTwo is not the player, lets remove it from the players client.
                            Tile* tile = spectatorTwo->getTile();
                            const Position& tilePosition = tile->getPosition();
                            oldStackPosVector.push_back(tmpPlayer->canSeeCreature(spectatorTwo) ? tile->getClientIndexOfCreature(tmpPlayer, spectatorTwo) : -1);
                            tmpPlayer->sendRemoveTileCreature(spectatorTwo, tilePosition, oldStackPosVector[i++]);
                        }
                    }
                }
            }
            else {
                // Spectator is a player but is not the creature, lets remove the creature from their client
                std::vector<int32_t> oldStackPosVector;
                size_t i = 0;
                if (tmpPlayer->canSeeCreature(creature)) {
                    Tile* tile = creature->getTile();
                    const Position& tilePosition = tile->getPosition();
                    oldStackPosVector.push_back(tmpPlayer->canSeeCreature(creature) ? tile->getClientIndexOfCreature(tmpPlayer, creature) : -1);
                    tmpPlayer->sendRemoveTileCreature(creature, tilePosition, oldStackPosVector[i++]);
                }
            }
        }
    }

    // Set creatures new instance ID
    creature->setInstanceID(instanceID);

    // Populate creature on players screens in same instance
    for (Creature* spectator : spectators) {
        if (Player* tmpPlayer = spectator->getPlayer()) {
            if (tmpPlayer->getID() != creature->getID()) {
                if (tmpPlayer->canSeeCreature(creature)) {
                    // Spectator is not the creature and can see the creature : send the creature to spectators client
                    tmpPlayer->sendCreatureAppear(creature, creature->getPosition(), true);
                }
            }
            else {
                // Spectator is the creature changing instance : Lets populate his client with other creatures in instance
                for (Creature* spectatorTwo : spectators) {
                    if (Player* tmpTarget = spectatorTwo->getPlayer()) {
                        if (tmpPlayer->getID() != tmpTarget->getID()) {
                            if (tmpPlayer->canSeeCreature(tmpTarget)) {
                                // Target is not the spectator and can be seen by the spectator : Send the target to his client.
                                tmpPlayer->sendCreatureAppear(tmpTarget, tmpTarget->getPosition(), true);
                            }
                        }
                    }
                    else {
                        tmpPlayer->sendCreatureAppear(spectatorTwo, spectatorTwo->getPosition(), true);
                    }
                }
            }
        }
    }

    for (Creature* summon : creature->summons) {
        changeInstance(summon, instanceID);
    }

    return true;
}

player.cpp
find
C++:
bool Player::canSeeCreature(const Creature* creature) const
at bottom of method add
C++:
if (creature->getInstanceID() != getInstanceID() || getInstanceID() != creature->getInstanceID()) {
        return false;
    }

luascript.h
under
C++:
static int luaGameCreateMonster(lua_State* L);
add
C++:
static int luaGameCreateInstanceMonster(lua_State* L);
under
C++:
static int luaGameCreateNpc(lua_State* L);
add
C++:
static int luaGameCreateInstanceNpc(lua_State* L);
under
C++:
static int luaCreatureCanSeeInvisibility(lua_State* L);
add
C++:
static int luaCreatureChangeInstance(lua_State* L);
static int luaCreatureGetInstance(lua_State* L);

luascript.cpp
under
C++:
registerMethod("Game", "createMonster", LuaScriptInterface::luaGameCreateMonster);
add
C++:
registerMethod("Game", "createInstanceMonster", LuaScriptInterface::luaGameCreateInstanceMonster);
under
C++:
registerMethod("Game", "createNpc", LuaScriptInterface::luaGameCreateNpc);
add
C++:
registerMethod("Game", "createInstanceNpc", LuaScriptInterface::luaGameCreateInstanceNpc);

under
C++:
registerMethod("Creature", "canSeeInvisibility", LuaScriptInterface::luaCreatureCanSeeInvisibility);
add
C++:
registerMethod("Creature", "changeInstance", LuaScriptInterface::luaCreatureChangeInstance);
registerMethod("Creature", "getInstance", LuaScriptInterface::luaCreatureGetInstance);
under method
C++:
int LuaScriptInterface::luaGameCreateUniqueMonster(lua_State* L)
add
C++:
int LuaScriptInterface::luaGameCreateInstanceMonster(lua_State* L)
{
    // Game.createInstanceMonster(monsterName, position[, instanceId[, extended = false[, force = false]]])
    Monster* monster = Monster::createMonster(getString(L, 1));
    if (!monster) {
        lua_pushnil(L);
        return 1;
    }

    const Position& position = getPosition(L, 2);
    uint32_t instanceId = getNumber<uint32_t>(L, 3, 0);
    bool extended = getBoolean(L, 4, false);
    bool force = getBoolean(L, 5, false);
    if (g_game.placeInstanceCreature(monster, position, instanceId, extended, force)) {
        pushUserdata<Monster>(L, monster);
        setMetatable(L, -1, "Npc");
    }
    else {
        delete monster;
        lua_pushnil(L);
    }
    return 1;
}

int LuaScriptInterface::luaGameCreateInstanceNpc(lua_State* L)
{
    // Game.createInstanceNpc(npcName, position[, instanceId[, extended = false[, force = false]]])
    Npc* npc = Npc::createNpc(getString(L, 1));
    if (!npc) {
        lua_pushnil(L);
        return 1;
    }

    const Position& position = getPosition(L, 2);
    uint32_t instanceId = getNumber<uint32_t>(L, 3, 0);
    bool extended = getBoolean(L, 4, false);
    bool force = getBoolean(L, 5, false);
    if (g_game.placeInstanceCreature(npc, position, instanceId, extended, force)) {
        pushUserdata<Npc>(L, npc);
        setMetatable(L, -1, "Npc");
    }
    else {
        delete npc;
        lua_pushnil(L);
    }
    return 1;
}

int LuaScriptInterface::luaCreatureGetInstance(lua_State* L)
{
    // creature:getInstance()
    Creature* creature = getUserdata<Creature>(L, 1);
    if (!creature) {
        lua_pushnil(L);
        return 1;
    }

    uint32_t instanceID = creature->getInstanceID();
    lua_pushnumber(L, instanceID);
    return 1;
}

int LuaScriptInterface::luaCreatureChangeInstance(lua_State* L)
{
    // creature:changeInstance(id)
    Creature* creature = getUserdata<Creature>(L, 1);
    if (!creature) {
        lua_pushnil(L);
        return 1;
    }

    uint32_t instanceID = getNumber<uint32_t>(L, 2);
    if (!instanceID) {
        lua_pushnil(L);
    }

    if (!g_game.changeInstance(creature, instanceID)) {
        lua_pushnil(L);
        return 1;
    }

    lua_pushboolean(L, true);
    return 1;
}

Those last functions should be moved to the correct place to keep it clean. You don't need to though. You can easily change creatures instances at anytime, anywhere. Hope someone enjoys.
Thanks for contributing really you did excellent work!!
 

Fresh

Quack!
Joined
Oct 21, 2009
Messages
1,764
Solutions
18
Reaction score
490
Location
Poland
Who will want, will do what he wants with it Mr. Proen,
@Itutorial thanks for doing something and sharing it, bless mate.
 
Last edited:
OP
OP
Itutorial

Itutorial

Excellent OT User
Joined
Dec 23, 2014
Messages
2,272
Solutions
68
Reaction score
907
So delusional.
Bro, I get why you point the problems out but that's all you do. I am also aware, or am pretty, sure you contribute to the master branch which is fine. I don't go to an artist and say "Here all the shit that's wrong with your picture." especially when he is giving the picture to charity in hopes to make someone some money.

You are the delusional one. Its fine to highlight problems but its how you do it. Why not offer a solution along with the list of problems. I released the beginning of code that can help a lot of people. You pointed out problems. You think what you did was better?
 

oen432

Legendary OT User
Joined
Oct 3, 2014
Messages
1,629
Solutions
54
Reaction score
1,752
Location
Poland
GitHub
Oen44
Bro, I get why you point the problems out but that's all you do. I am also aware, or am pretty sure you contribute to the master branch which is fine. I don't go to an artist and say "Here all the shit that's wrong with your picture." especially when he is giving the picture to charity in hopes to make someone some money.

You are the delusional one. Its fine to highlight problems but its how you do it. Why not offer a solution along with the list of problems. I released the beginning of code that can help a lot of people. You pointed out problems. You think what you did was better?
Its like you can't point flaws in other people work, damn, fucking snowflakes.

Anyway... back to making money.
 
OP
OP
Itutorial

Itutorial

Excellent OT User
Joined
Dec 23, 2014
Messages
2,272
Solutions
68
Reaction score
907
Its like you can't point flaws in other people work, damn, fucking snowflakes.

Anyway... back to making money.
I am just saying you are annoying. You are the one calling me names snowflake.

Almost done fixing the "bottom of the iceberg"
Post automatically merged:

I had to add a fix in game.cpp the function changeInstance was modified. Make sure if anyone did add this to fix it. The code is fixed on the main post.
Post automatically merged:

Sad to say but the rest of this system did take quite a bit of code. I won't be releasing it just because of how much is needed to be included.

I will say, You just need to modify places with getSpectators and addMagicEffect/sendMagicEffect and distance effects
and add instanceId onto items to be able to modify which items can be seen by players in the instances.

Text messages are handled through the getSpectators so that will be handled once you do that.

Its not difficult but there is quite a bit to change.

To make it easy, just make it so instanceId = 0 by default on them, and change all places that have the said methods to include checking the instanceId.
 
Last edited:

zbizu

Legendary OT User
Joined
Nov 22, 2010
Messages
3,237
Solutions
25
Reaction score
2,443
Location
Poland
GitHub
Zbizu
do you use separate stacks for every instance tiles?

pretty sure there can be stackpos issues if you don't
 

oen432

Legendary OT User
Joined
Oct 3, 2014
Messages
1,629
Solutions
54
Reaction score
1,752
Location
Poland
GitHub
Oen44
pretty sure there can be stackpos issues if you don't
There will be. I already made instancing system few years ago that uses IDs like this one, it does require more work than just preventing ProtocolGame to send data to player with different instance id.
 
Last edited:
OP
OP
Itutorial

Itutorial

Excellent OT User
Joined
Dec 23, 2014
Messages
2,272
Solutions
68
Reaction score
907
do you use separate stacks for every instance tiles?

pretty sure there can be stackpos issues if you don't
I haven't started the code for items. I will work on it tomorrow and let you know if I get it working. So far I worked on text messages, magic effects, corpses, blockhit, distance effects, talking/yelling, ect.
 
OP
OP
Itutorial

Itutorial

Excellent OT User
Joined
Dec 23, 2014
Messages
2,272
Solutions
68
Reaction score
907
Yeah I saw that too back when I made it. What it does is checks the creatures instance vs the players, then checks the players vs the creatures. It shouldn't ever get to the second part. Glad I could help you understand.
Post automatically merged:

There will be. I already made instancing system few years ago that uses IDs like this one, it does require more work than just preventing ProtocolGame to send data to player with different instance id.
Yet you came and pointed out problems with mine and didn't release yours.
 
OP
OP
Itutorial

Itutorial

Excellent OT User
Joined
Dec 23, 2014
Messages
2,272
Solutions
68
Reaction score
907
Because I'm done releasing stuff for free for this community full of leechers, they deserve nothing. Other than maintaining OTCv8, I'm not contributing to anything anymore.
Why even be here then? Are you a leecher?
 
Top