So I've been working on the source for a little while now fixing all of the bugs and what not.. And I've come a real long way..
But I've ran into an issue that I simply cannot wrap my head around.
There's an issue with player visibility.
Essentially, when jumping at specific angles at specific moments (lag screen) - sometimes one (or both) entities will vanish to each other.
If I force UpdateSurroundings(true) in Entity.cs - this will maintain visibility and or restore the invisible entity, but this is obviously not a viable solution as I don't want to have the players visibility dictionary being reset constantly.. It was more of a debugging exercise than anything else.
From what I can tell, it's almost as if there's a 'dead zone' where the players jump from->to that causes it, where the screen queries are not capturing and updating the entity correctly.. I've tried so many things now but just hitting brick walls.
Thanks, I appreciate the help.
I think the relevant files are Entity.cs, Map.cs and MapManager.cs that appear to handle the visibility logic. There are obviously numerous calculation files too.
Has anybody managed to fix this issue and can help me please?
I've never worked with Redux. But on my own sources I remember there is a packet that can be sent to remove an entity from a player's screen. Maybe need to check when that is being send during client movements. Perhaps the math is a bit off and the packet is being sent while clients are still in view of eachother which causes them to just disappear.
I've never worked with Redux. But on my own sources I remember there is a packet that can be sent to remove an entity from a player's screen. Maybe need to check when that is being send during client movements. Perhaps the math is a bit off and the packet is being sent while clients are still in view of eachother which causes them to just disappear.
To note of the packet TK mentioned, this should only be used if the entity needs to be forcibly removed from surrounding entities' screens. This includes teleporting, or logging off. This should not be used when a character walks or jumps off screen. The MsgWalk and MsgAction(MapJump subtype 133)will handle removing entities for movement.
Edit: Take a look at Spirited's documentation on screen. Can help out with understanding how the screen system works when implemented appropriately.
To note of the packet TK mentioned, this should only be used if the entity needs to be forcibly removed from surrounding entities' screens. This includes teleporting, or logging off. This should not be used when a character walks or jumps off screen. The MsgWalk and MsgAction(MapJump subtype 133)will handle removing entities for movement.
Edit: Take a look at Spirited's documentation on screen. Can help out with understanding how the screen system works when implemented appropriately.
Thanks Arco, I very much appreciate that. I'll take a look!
To note of the packet TK mentioned, this should only be used if the entity needs to be forcibly removed from surrounding entities' screens. This includes teleporting, or logging off. This should not be used when a character walks or jumps off screen. The MsgWalk and MsgAction(MapJump subtype 133)will handle removing entities for movement.
Edit: Take a look at Spirited's documentation on screen. Can help out with understanding how the screen system works when implemented appropriately.
Quote:
Originally Posted by tkblackbelt
I've never worked with Redux. But on my own sources I remember there is a packet that can be sent to remove an entity from a player's screen. Maybe need to check when that is being send during client movements. Perhaps the math is a bit off and the packet is being sent while clients are still in view of eachother which causes them to just disappear.
Nope, I still haven't been able to resolve my issue (nor have some of the other folks I asked who are looking at it).
Out of interest, I downloaded and tested some other base sources too, and this appears to be a very common problem (surprised more people haven't talked about it!)
Nope, I still haven't been able to resolve my issue (nor have some of the other folks I asked who are looking at it).
Out of interest, I downloaded and tested some other base sources too, and this appears to be a very common problem (surprised more people haven't talked about it!)
Any other ideas/areas I can look at perhaps?
Without seeing any code that relates to this, it'll be hard for us to help pinpoint the cause of the issue.
Without seeing any code that relates to this, it'll be hard for us to help pinpoint the cause of the issue.
Well, the base Redux source has the issue (or albetros) - it's hard to pinpoint the exact piece of code that's causing the problem.
It could be a Network/Synch issue, or something wrong in the logic.. If UpdateSurroundings in the Entity class is set (true) it will constantly reset visibility - but respawning the entities on every jump is certainly not appropriate. I can't increase the visible range more than 18 in map class. I've tried handling corners (unless I did it wrong). I've tried adding a visibility buffer > 18 to force a reset.. I dunno, I can't see what I haven't tried or what I'm missing
Player.cs
Code:
public void HandleJump(GeneralActionPacket packet)
{
if (Life <= 0)
{
return;
}
if (Common.MapService.Valid((ushort)Map.ID, X, Y, packet.Data1Low, packet.Data1High))
{
X = packet.Data1Low;
Y = packet.Data1High;
OnMove();
SendToScreen(packet, true);
UpdateSurroundings();
if (Pet != null && Common.Clock - Pet.LastMove > 900)
Pet.LastMove = Common.Clock + 300;
}
else
Send(new GeneralActionPacket()
{
UID = this.UID,
Data1 = Map.ID,
Data2Low = X,
Data2High = Y,
Action = DataAction.NewCoordinates,
});
}
Entity.cs:
Code:
public void UpdateSurroundings(bool clear = false)
{
if (clear)
VisibleObjects = new ConcurrentDictionary<uint, uint>();
List<uint> newObjects = new List<uint>();
foreach (var target in Map.QueryScreen(this))
if (target.UID != UID)
newObjects.Add(target.UID);
//Remove existing objects if they are no longer visible
var visibleObjectsToRemove = VisibleObjects.Keys.Except(newObjects).ToList();
foreach (var id in visibleObjectsToRemove)
MapManager.DespawnByUID(this, id);
//Remove new objects if they exist already
foreach (var id in VisibleObjects.Keys)
if (newObjects.Contains(id))
newObjects.Remove(id);
//Spawn new objects
foreach (var id in newObjects)
MapManager.SpawnByUID(this, id);
}
MapManager.cs:
Code:
public static void DespawnByUID(Entity p, uint id)
{
var t = p.Map.Search(id);
if (t == null || t == p)
return;
if (p is Player && t is Monster)//Player can no longer see monster
{
var m = (Monster)t;
var found = false;
foreach (var objID in m.VisibleObjects)
if (objID.Key > 1000000)
{ found = true; break; }
if (found)
m.IsActive = true;
else
{
var isSpawnActive = false;
foreach (var spawnMember in m.Owner.AliveMembers.Values)
if (spawnMember.IsActive)
{ isSpawnActive = true; break; }
m.Owner.IsActive = isSpawnActive;
}
m.IsActive = found;
}
if (t is Entity)
{
var target = t as Entity;
if (target.VisibleObjects != null && target.VisibleObjects.ContainsKey(p.UID))
{
uint x;
target.VisibleObjects.TryRemove(p.UID, out x);
}
}
if (p.VisibleObjects != null && p.VisibleObjects.ContainsKey(t.UID))
{
uint x;
p.VisibleObjects.TryRemove(t.UID, out x);
}
}
/// <summary>
/// Spawn entities to eachother as well as spawning basic objects (items, npcs, sobs, traps) to players
/// </summary>
/// <param name="p">player to see spawned objects</param>
/// <param name="id">uid of object to spawn</param>
public static void SpawnByUID(Entity p, uint id)
{
var sob = p.Map.Search<SOB>(id);
if (sob != null)
{
if (p is Player && !p.VisibleObjects.ContainsKey(sob.UID))
{
((Player)p).Send(Packets.Game.SpawnSob.Create(sob));
p.VisibleObjects.TryAdd(sob.UID, sob.UID);
}
return;
}
var target = p.Map.Search<Entity>(id);
if (target != sob && target != null && target != p)
{
if (!p.VisibleObjects.ContainsKey(target.UID))
{
p.VisibleObjects.TryAdd(target.UID, target.UID);
p.Send(target.SpawnPacket);
}
if (!target.VisibleObjects.ContainsKey(p.UID))
{
if (p is Player && target is Monster)
{
var m = (Monster)target;
if (m.Owner != null)
{
m.IsActive = true;
m.Owner.IsActive = true;
}
}
target.VisibleObjects.TryAdd(p.UID, p.UID);
if (p is Player && target is Player)
{
if (!p.VisibleObjects.ContainsKey(target.UID))
{
p.VisibleObjects.TryAdd(target.UID, target.UID);
p.Send(target.SpawnPacket);
}
}
target.Send(p.SpawnPacket);
if (target is Player && p is Player)
if ((target as Player).Shop != null && (target as Player).Shop.Vending && (target as Player).Shop.HawkMsg.Words.Length > 1)
p.Send((target as Player).Shop.HawkMsg);
}
return;
}
var npc = p.Map.Search<Npc>(id);
if (npc != null)
{
if (p is Player && !p.VisibleObjects.ContainsKey(npc.UID))
{
((Player)p).Send(npc.SpawnPacket);
p.VisibleObjects.TryAdd(npc.UID, npc.UID);
}
return;
}
var item = p.Map.Search<GroundItem>(id);
if (item != null)
{
if (p is Player && !p.VisibleObjects.ContainsKey(item.UID))
{
((Player)p).Send(item.SpawnPacket);
p.VisibleObjects.TryAdd(item.UID, item.UID);
}
return;
}
}
Map.cs:
Code:
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using Redux.Utility;
using Redux.Database;
using Redux.Database.Domain;
using Redux.Game_Server;
using Redux.Managers;
using Redux.Enum;
namespace Redux.Space
{
#region Map Class
public class Map
{
#region Variables
public bool IsActive = true;
private List<DbDropRule> _mineRules;
public List<DbDropRule> MineRules { get { return _mineRules; } }
public uint ID { get; private set; }
public uint DynamicID { get; private set; }
public DbMap MapInfo { get; private set; }
public ConcurrentDictionary<uint, ILocatableObject> Objects { get; private set; }
public List<SpawnManager> Spawns { get; private set; }
public ThreadSafeCounter ItemCounter, MobCounter, SobCounter, PetCounter;
#endregion
#region Constructor
/// <summary>
/// Initialize a new Map
/// </summary>
/// <param name="dynamicID">Key to identify map</param>
/// <param name="id">Dmap value of map. Used for all movement checks</param>
public Map(uint _dynamicID, uint _id)
{
Objects = new ConcurrentDictionary<uint, ILocatableObject>();
ItemCounter = new ThreadSafeCounter(100000, 150000);
SobCounter = new ThreadSafeCounter(150000, 200000);
MobCounter = new ThreadSafeCounter(400000, 500000);
PetCounter = new ThreadSafeCounter(500000, 600000);
MapInfo = ServerDatabase.Context.Maps.GetById(_id);
ID = _id;
DynamicID = _dynamicID;
var dNpcs = ServerDatabase.Context.Npcs.GetNpcsByMap((ushort)_dynamicID);
if (dNpcs.Count > 0)
foreach (var dNpc in dNpcs)
Insert(new Npc(dNpc, this));
var dSpawns = ServerDatabase.Context.Spawns.GetSpawnsByMap((ushort)_dynamicID);
Spawns = new List<SpawnManager>();
if (dSpawns.Count > 0)
foreach (var dSpawn in dSpawns)
Spawns.Add(new SpawnManager(dSpawn, this));
foreach (var dSob in ServerDatabase.Context.SOB.GetSOBByMap((ushort)_dynamicID))
Insert(new SOB(dSob));
if (MapInfo.Type.HasFlag(MapTypeFlags.MineEnable))
_mineRules = ServerDatabase.Context.DropRules.GetRulesByMonsterType(ID).ToList();
Console.WriteLine("Map ID {0} loaded {1} npcs and {2} spawns", _dynamicID, dNpcs.Count, dSpawns.Count);
}
#endregion
#region Functions
public bool IsTGMap { get { return ID == 1039; } }
public bool IsNoScrollEnabled { get { return MapInfo.Type.HasFlag(Enum.MapTypeFlags.TeleportDisable); } }
public bool IsFlyEnabled { get { return !MapInfo.Type.HasFlag(Enum.MapTypeFlags.FlyDisable); } }
public bool IsPKEnabled { get { return !MapInfo.Type.HasFlag(Enum.MapTypeFlags.PkDisable); } }
public bool IsNeverWound { get { return MapInfo.Type.HasFlag(Enum.MapTypeFlags.NeverWound); } }
public bool IsFreePK { get { return MapInfo.Type.HasFlag(Enum.MapTypeFlags.PkField) || MapInfo.Type.HasFlag(Enum.MapTypeFlags.FreePk); } }
public bool IsGuildMap { get { return MapInfo.Type.HasFlag(Enum.MapTypeFlags.GuildMap); } }
public IEnumerable<ILocatableObject> QueryScreen(ILocatableObject objCenter)
{
var area = GetEntityScreenArea(objCenter);
var query = from x in Objects.Values
where area.AreaContains(x.Location)
select x;
return query;
}
public IEnumerable<T> QueryScreen<T>(ILocatableObject objCenter)
{
var area = GetEntityScreenArea(objCenter);
var query = from x in Objects.Values
where area.AreaContains(x.Location) && x is T
select (T)x;
return query;
}
public IEnumerable<T> QueryBox<T>(ILocatableObject _objCenter, int _size)
{
var area = new Rectangle(new Point(_objCenter.Location.X - _size, _objCenter.Location.Y - _size), _size * 2, _size * 2);
var query = from x in Objects.Values
where area.AreaContains(x.Location) && x is T
select (T)x;
return query;
}
public IEnumerable<Entity> QueryBox(int _x, int _y, int _width, int _height)
{
var area = new Rectangle(_x, _y, _width, _height);
var query = from x in Objects.Values
where area.AreaContains(x.Location) && x is Entity
select x as Entity;
return query;
}
public IEnumerable<Game_Server.Player> QueryPlayers(ILocatableObject objCenter)
{
var area = GetEntityScreenArea(objCenter);
var query = from x in Objects.Values
where area.AreaContains(x.Location) && x is Game_Server.Player
select x as Game_Server.Player;
return query;
}
public Rectangle GetEntityScreenArea(ILocatableObject objCenter)
{
int visibilityRange = 18; // This can be adjusted
return new Rectangle(new Point(objCenter.Location.X - visibilityRange, objCenter.Location.Y - visibilityRange), 2 * visibilityRange, 2 * visibilityRange);
}
public bool Insert(ILocatableObject obj)
{
if (obj == null)
return false;
if (Objects.ContainsKey(obj.UID))
return false;
obj.Map = this;
Objects.TryAdd(obj.UID, obj);
return true;
}
public bool Remove(ILocatableObject obj, bool updatelocal = true)
{
if (updatelocal)
{
if (obj is Entity)
{
var entity = obj as Entity;
entity.SendToScreen(new Packets.Game.GeneralActionPacket() { UID = entity.UID, Action = Enum.DataAction.RemoveEntity });
foreach (var id in entity.VisibleObjects.Keys)
Managers.MapManager.DespawnByUID(entity, id);
}
}
return Objects.TryRemove(obj.UID, out obj);
}
public ILocatableObject Search(uint uid)
{
lock (Objects)
{
var query = from x in Objects.Values
where x.UID == uid
select x;
return query.FirstOrDefault();
}
}
public T Search<T>(uint id)
where T : ILocatableObject
{
if (id != 0)
foreach (var target in Objects)
if (target.Value is T)
if (target.Key == id)
return (T)target.Value;
return default(T);
}
public IEnumerable<KeyValuePair<uint, ILocatableObject>> Select(Func<KeyValuePair<uint, ILocatableObject>, bool> predicate)
{
return Objects.Where(predicate);
}
public bool IsValidMonsterLocation(Point location)
{
return Common.MapService.Valid((ushort)ID, (ushort)location.X, (ushort)location.Y) && !Common.MapService.HasFlag((ushort)ID, (ushort)location.X, (ushort)location.Y, TinyMap.TileFlag.Monster);
}
public bool IsValidItemLocation(Point location)
{
return Common.MapService.Valid((ushort)ID, (ushort)location.X, (ushort)location.Y) && !Common.MapService.HasFlag((ushort)ID, (ushort)location.X, (ushort)location.Y, TinyMap.TileFlag.Item);
}
public bool IsValidPlayerLocation(Point location)
{
return Common.MapService.Valid((ushort)ID, (ushort)location.X, (ushort)location.Y);
}
#endregion
}
#endregion
}
but respawning the entities on every jump is certainly not appropriate.
You don't need to respawn entities every jump, but you should be parsing the screen with every movement. In my Update method, I parse the screen for any entity that should be removed, then remove them from the collection.
Then parse through the map's entities, checks if the owner of the screen can see them, then executes that Add function. In my add method, I add the queried entity to the collection, and add the owner to that entity's collection.
Not respawning entities that are already present. And only removing from collection as needed.
Edit: Parameters passed through are only used for forcibly removing entities from a screen such as teleporting or logging off.
Code:
/// <summary>
/// Updates the entities in the screen's view based on the owner's position and visibility range.
/// </summary>
public async Task UpdateAsync(bool clear = false, bool endSession = false)
{
if (!ServerCollections.TryGetMap(owner.MapID, out var map))
return;
if (clear || endSession)
{
foreach (var targetEntity in entitiesInView.Values)
{
await RemoveAsync(targetEntity);
if (targetEntity is Character targetCharacter)
{
//Maybe rmeove
await targetCharacter.SendAsync(MsgAction.DespawnEntity(owner));
}
}
entitiesInView.Clear();
if (endSession)
return;
}
var entitiesToRemove = entitiesInView.Values
.Where(entity => !CalculationEngine.CanSee(owner, entity, 18))
.ToList();
foreach (var targetEntity in entitiesToRemove)
{
await RemoveAsync(targetEntity);
}
var entitiesToAdd = map.GetAllEntities()
.Where(entity => CalculationEngine.CanSee(owner, entity, 18))
.Where(entity => !entitiesInView.ContainsKey(entity.UniqueID))
.Where(entity => entity != owner)
.ToList();
foreach (var targetEntity in entitiesToAdd)
{
await AddAsync(targetEntity);
}
//Should only support characters and mobs as owners.
}
You don't need to respawn entities every jump, but you should be parsing the screen with every movement. In my Update method, I parse the screen for any entity that should be removed, then remove them from the collection.
Then parse through the map's entities, checks if the owner of the screen can see them, then executes that Add function. In my add method, I add the queried entity to the collection, and add the owner to that entity's collection.
Not respawning entities that are already present. And only removing from collection as needed.
Edit: Parameters passed through are only used for forcibly removing entities from a screen such as teleporting or logging off.
Code:
/// <summary>
/// Updates the entities in the screen's view based on the owner's position and visibility range.
/// </summary>
public async Task UpdateAsync(bool clear = false, bool endSession = false)
{
if (!ServerCollections.TryGetMap(owner.MapID, out var map))
return;
if (clear || endSession)
{
foreach (var targetEntity in entitiesInView.Values)
{
await RemoveAsync(targetEntity);
if (targetEntity is Character targetCharacter)
{
//Maybe rmeove
await targetCharacter.SendAsync(MsgAction.DespawnEntity(owner));
}
}
entitiesInView.Clear();
if (endSession)
return;
}
var entitiesToRemove = entitiesInView.Values
.Where(entity => !CalculationEngine.CanSee(owner, entity, 18))
.ToList();
foreach (var targetEntity in entitiesToRemove)
{
await RemoveAsync(targetEntity);
}
var entitiesToAdd = map.GetAllEntities()
.Where(entity => CalculationEngine.CanSee(owner, entity, 18))
.Where(entity => !entitiesInView.ContainsKey(entity.UniqueID))
.Where(entity => entity != owner)
.ToList();
foreach (var targetEntity in entitiesToAdd)
{
await AddAsync(targetEntity);
}
//Should only support characters and mobs as owners.
}
Thanks, that's helpful.
My problem is (I think) - is that because the client automatically despawns the entity when it leaves the screen, there's this very specific moment where if player A is chasing player B, if they jump at the same time, player A goes invisible to player B, but the visibilitydictionary is not updated because technically, player A can still see player B....
Therefore Player A is not considered a newObject, and therefore not spawned back into the screen of Player B when it arrives... Does that make sense?
I can't figure out how to reliably capture Player A and force it to be considered a newObject at that very specific moment, so that it's respawned onto the screen of Player B... :/
The UpdateSurroundings method should be acting as you mentioned, whereby it's querying the screen for entities and adding/removing them based on their range from the player (18 units), but there's just that very specific instance/moment where this does not occur and therefore the entity is not captured and considered a newObject and therefore respawned to the targets screen.
I'll have a look at what you've said above and try it out! Thanks again I appreciate it!!
Short vid of the issue (I've got fairly good at replicating it..).
if they jump at the same time, player A goes invisible to player B, but the visibilitydictionary is not updated because technically, player A can still see player B....
Korvacs and Arco honestly had me audibly laughing out loud. @, if you're still around, hope you're living your best life J-Man.
These actions are happening sequentially. One after the other, and these checks are happening in **** near nanoseconds. It's quite unlikely that something is happening at a very specific moment when speaking computationally.
Upon watching the clip, the spot that Korvacs is in is still within view of Arco after Arco completes his first jump. Why is Korvacs being removed from screen is the question? I'd like to see your functions that relate to jumping. I feel like there is a possibility that the issue may lie there.
Not really around, around @ but I pop in every now and again. Funnily enough I was looking at this recently for someone, though that's not me in this video which is weird lol.
The client isn't being despawned by the server, it doesn't send the packet, I think there is a moment where when both clients jump, the Korvacs client ends up more than 18 tiles away from the Arco client, so the Korvacs client despawns Arco, whereas the server considers that to be valid so doesn't remove the client from the screen because from the server perspective they're always within 18 tiles of each other.
I can't decide if it's just a thing with the 5065 client or not.
What also confused me is that I would expect the client to send the RequestEntity/Sync General Data packet to spawn the client as it's receiving jump packets for a client it can't see, but it never does.
Korvacs and Arco honestly had me audibly laughing out loud. @, if you're still around, hope you're living your best life J-Man.
These actions are happening sequentially. One after the other, and these checks are happening in damn near nanoseconds. It's quite unlikely that something is happening at a very specific moment when speaking computationally.
Upon watching the clip, the spot that Korvacs is in is still within view of Arco after Arco completes his first jump. Why is Korvacs being removed from screen is the question? I'd like to see your functions that relate to jumping. I feel like there is a possibility that the issue may lie there.
Code:
public void HandleJump(GeneralActionPacket packet)
{
if (Life <= 0)
{
return;
}
if (Common.MapService.Valid((ushort)Map.ID, X, Y, packet.Data1Low, packet.Data1High))
{
X = packet.Data1Low;
Y = packet.Data1High;
OnMove();
SendToScreen(packet, true);
UpdateSurroundings();
if (Pet != null && Common.Clock - Pet.LastMove > 900)
Pet.LastMove = Common.Clock + 300;
}
else
Send(new GeneralActionPacket()
{
UID = this.UID,
Data1 = Map.ID,
Data2Low = X,
Data2High = Y,
Action = DataAction.NewCoordinates,
});
}
Haha, it seemed fitting . That's the jump.
Quote:
Originally Posted by Korvacs
Not really around, around @ but I pop in every now and again. Funnily enough I was looking at this recently for someone, though that's not me in this video which is weird lol.
The client isn't being despawned by the server, it doesn't send the packet, I think there is a moment where when both clients jump, the Korvacs client ends up more than 18 tiles away from the Arco client, so the Korvacs client despawns Arco, whereas the server considers that to be valid so doesn't remove the client from the screen because from the server perspective they're always within 18 tiles of each other.
I can't decide if it's just a thing with the 5065 client or not.
What also confused me is that I would expect the client to send the RequestEntity/Sync General Data packet to spawn the client as it's receiving jump packets for a client it can't see, but it never does.
Edit: mentioning is super janky jfc
I was wondering this too about the 5065 thing (but albetros does it too and that's 55**?).. I've managed to make it harder to go invisible, but that's as close as I've gotten to a fix ha.
public void HandleJump(GeneralActionPacket packet)
{
if (Life <= 0)
{
return;
}
if (Common.MapService.Valid((ushort)Map.ID, X, Y, packet.Data1Low, packet.Data1High))
{
X = packet.Data1Low;
Y = packet.Data1High;
OnMove();
SendToScreen(packet, true);
UpdateSurroundings();
if (Pet != null && Common.Clock - Pet.LastMove > 900)
Pet.LastMove = Common.Clock + 300;
}
else
Send(new GeneralActionPacket()
{
UID = this.UID,
Data1 = Map.ID,
Data2Low = X,
Data2High = Y,
Action = DataAction.NewCoordinates,
});
}
First update surroundings and don't broadcast the Walk or Jump packet, send them individually. Whos entering the screen needs to receive your movement packets and who's entering your screen must receive it too.
If you broadcast the packet before updating your surroundings, whos in your screen will receive it, but who's entering your screen will not. And if you broadcast them twice, you can guess what will happen.
First update surroundings and don't broadcast the Walk or Jump packet, send them individually. Whos entering the screen needs to receive your movement packets and who's entering your screen must receive it too.
If you broadcast the packet before updating your surroundings, whos in your screen will receive it, but who's entering your screen will not. And if you broadcast them twice, you can guess what will happen.
Thanks for the help i appreciate it. I'm a bit confused by what you mean.. So I don't send the broadcast packet at all? Or I do - but after I send the individual packets?
So -> UpdateSurroundings (sort visibledictionaries etc)
Send -> individual packets to all objects on screen..
-> No need to broadcast as everyone gets the individual packets?
PLEASE PLEASE PLEASE PLEASE PLEASE PLEASE PLEASE PLEASE 06/21/2011 - Metin2 Private Server - 5 Replies HELP WITH THIS PLEASE ON MY DEDICATED SERVER :-(
http://www.youtube.com/watch?v=UyKOTLb960Q
log_file_delete_old: stat: No such file or directory
connect: Connection refused
Timed out
Connection refused