Rezlind
Member-
Posts
82 -
Joined
-
Last visited
Content Type
Profiles
Forums
Downloads
Everything posted by Rezlind
-
It sounds like it would be easy enough for you to add that yourself? What is stopping you?
-
How to remove multiple variety of items from inventory
Rezlind replied to ownedrpd's topic in Conquer Online
In C# && and || are used when attempting to boolean shortcircuit. I reckon in the code above the first item 711306 is removed but the second item 711307 is not removed. You want to perform the removals in sequence on two separate lines instead: client.Inventory.Remove(711306, 1, stream); client.Inventory.Remove(711307, 1, stream); When you do it in the fashion you showed above, C# will stop executing at the first truthy evaluation, and if .Remove returns truthy on the first removal (which I imagine it does in your code since rarely will you sanity check a removal), it will not bother executing the second removal. Try the above and let me know what happens. -
For issue # 1 - that looks like some kind of issue with the packet structure to be honest. Take another look at the packet and what the packet is suppose to be for clients at that version. Make sure you're using the correct client version. I used this source as the base for our source NotConquer but we upgraded pretty much immediately to 5017 and that did require touching the MsgTalk packet so I can't remember if we ever encountered this issue. In any case, I would set breakpoints on the MsgTalk packet and see what the actual string receieved server side is. It could be a problem with how your server is parsing the packet. For issue # 2 - I don't know what PvP toggle button you're talking about, in the version you pointed out as far as I remember there was only the capture button on the bottom right - are you saying that button doesn't open the sub menu? The resulting little subwindow may just have the wrong position set in the gui.ini. For Issue # 3 - Try using the /mm {mapId} {x} {y} command to teleport to lab, if the map exists it probably existed on that version. Attached you'll find the map ids.[/img] In general the Cops v6 enhanced source is an excellent source, you'll learn a lot just by working with it. It has a bit more overhead than other sources in terms of writing the packets etc... But it is clear, and concise. Let me know if you have any more questions, I'm happy to answer them.
-
Yeah, it's easy to feel like you're really unlucky in these situations but it's so difficult to test unless the owner adds unit tests to the socketing code. That's also another suggestioin for the owner to verify things are working as intended.
-
With all due respect, without access to the actual code so you can definitively see rates there is no way for you to confirm what is happening. With numbers like 3000 meteors for a higher probability to get a socket you won't be able to realistically emulate your tests in-game. I don't think there is necessarily anything wrong with the implementation. Your sample size from actual meteor upgrades in-game will be significantly smaller than how many automated tests you run. So far you stated you spammed about 1 sample size worth of the expected number of meteors in-game. It's not guaranteed, it's just likely you will make the socket within 3000 meteors. If this is a concern perhaps the owner can create a test server and lower the numbers so you can run a larger sample.
-
Nice! Happy you fixed it, good luck!
-
Great so you have a debugging point here, figure out why those coordinates are false when you load them in. Something is off with your maps which we had a hunch for but now you have the process by which you confirmed it.
-
Okay. This is get; So it's returning false on the bounds check and that is what leads to your kickback and the fall through to the else?
-
public class Floor { [Flags] public enum DMapPointFlag : byte { Invalid = 1, Monster = 2, Item = 4, RaceItem = 8 } public class Size { public int Width, Height; public Size(int width, int height) { Width = width; Height = height; } public Size() { Width = 0; Height = 0; } } public Size Bounds; public DMapPointFlag[,] Locations; public uint FloorMapID; public Floor(int width, int height, uint mapID) { FloorMapID = mapID; Bounds = new Size(width, height); Locations = new DMapPointFlag[width, height]; } public bool this[int x, int y, MapObjectType type, object obj = null] { get { if (y >= Bounds.Height || x >= Bounds.Width || x < 0 || y < 0) return false; DMapPointFlag filltype = Locations[x, y]; if (type == MapObjectType.InvalidCast) return (filltype & DMapPointFlag.Invalid) == DMapPointFlag.Invalid; if ((filltype & DMapPointFlag.Invalid) == DMapPointFlag.Invalid) return false; if (type == MapObjectType.Player) return true; else if (type == MapObjectType.Monster) return (filltype & DMapPointFlag.Monster) != DMapPointFlag.Monster; else if (type == MapObjectType.Item) return (filltype & DMapPointFlag.Item) != DMapPointFlag.Item; else if (type == MapObjectType.StaticEntity) return (filltype & DMapPointFlag.RaceItem) != DMapPointFlag.RaceItem; return false; } set { if (y >= Bounds.Height || x >= Bounds.Width || x < 0 || y < 0) return; DMapPointFlag filltype = Locations[x, y]; if (value) { if (type == MapObjectType.InvalidCast) TakeFlag(x, y, DMapPointFlag.Invalid); if (type == MapObjectType.Item) TakeFlag(x, y, DMapPointFlag.Item); if (type == MapObjectType.Monster) TakeFlag(x, y, DMapPointFlag.Monster); if (type == MapObjectType.StaticEntity) TakeFlag(x, y, DMapPointFlag.RaceItem); } else { if (type == MapObjectType.InvalidCast) AddFlag(x, y, DMapPointFlag.Invalid); if (type == MapObjectType.Item) AddFlag(x, y, DMapPointFlag.Item); if (type == MapObjectType.Monster) AddFlag(x, y, DMapPointFlag.Monster); if (type == MapObjectType.StaticEntity) AddFlag(x, y, DMapPointFlag.RaceItem); } } } public DMapPointFlag AddFlag(int x, int y, DMapPointFlag extraFlag) { Locations[x, y] |= extraFlag; return Locations[x, y]; } public DMapPointFlag TakeFlag(int x, int y, DMapPointFlag extraFlag) { Locations[x, y] &= ~extraFlag; return Locations[x, y]; } } This is my map.floor method. If you like you can add me on discord as well (Natalynn#4100) I do notice in my breakpoint, not sure if its normal? The coordinates: Breakpoint public bool this[int x, int y, MapObjectType type, object obj = null] and figure out where in the getter or setter its failing.
-
I'm not sure fully what you mean by 'fall through'. But I'm hope I'm right. However I was curious and did comment out the "teleport" function under the if statement and see what would happen, Basically just disconnects my client. I've just copied the entire portion of this code. It's checking: if (Map.Floor[new_X, new_Y, Game.MapObjectType.Player, null]) { otherwise it'll be this: } else { if (client.Entity.Mode == Game.Enums.Mode.None) { client.Entity.Teleport(client.Map.ID, client.Entity.X, client.Entity.Y); } I've included the entire portion of the start of the if down to the else. if (Map != null) { if (Map.Floor[new_X, new_Y, Game.MapObjectType.Player, null]) { if (Kernel.GetDistance(new_X, new_Y, client.Entity.X, client.Entity.Y) <= 16) { client.Entity.Action = Game.Enums.ConquerAction.Jump; client.Entity.Facing = Kernel.GetAngle(generalData.wParam1, generalData.wParam2, new_X, new_Y); client.Entity.X = new_X; client.Entity.Y = new_Y; if (client.Entity.MapID == CaptureTheFlag.MapID) CheckForFlag(client); client.SendScreen(generalData, true); client.Screen.Reload(generalData); if (client.Entity.MapID == CaptureTheFlag.MapID) { foreach (INpc current2 in client.Map.Npcs.Values) { if (current2.MapID == CaptureTheFlag.MapID && Kernel.GetDistance(client.Entity.X, client.Entity.Y, current2.X, current2.Y) < 17) { current2.SendSpawn(client); } } } if (client.Entity.MapID == 3856 && Kernel.SpawnNemesis2) { foreach (INpc Npc in client.Map.Npcs.Values) { if (Npc.MapID == 3856 && (Npc.UID == 3080) && Kernel.GetDistance(client.Entity.X, client.Entity.Y, Npc.X, Npc.Y) < 17) { Npc.SendSpawn(client); } } } if (client.Entity.MapID == 1927 && Kernel.SpawnBanshee2) { foreach (INpc Npc in client.Map.Npcs.Values) { if (Npc.MapID == 1927 && (Npc.UID == 2999) && Kernel.GetDistance(client.Entity.X, client.Entity.Y, Npc.X, Npc.Y) < 17) { Npc.SendSpawn(client); } } } if (client.Entity.MapID == 1020 && Kernel.Titan2) { foreach (INpc Npc in client.Map.Npcs.Values) { if (Npc.MapID == 1020 && (Npc.UID == 29996) && Kernel.GetDistance(client.Entity.X, client.Entity.Y, Npc.X, Npc.Y) < 17) { Npc.SendSpawn(client); } } } if (client.Entity.MapID == 1010 && Kernel.Ganoderma2) { foreach (INpc Npc in client.Map.Npcs.Values) { if (Npc.MapID == 1010 && (Npc.UID == 29995) && Kernel.GetDistance(client.Entity.X, client.Entity.Y, Npc.X, Npc.Y) < 17) { Npc.SendSpawn(client); } } } if (client.Entity.MapID == 3935 && Kernel.AlluringWitchHisCrystals2) { foreach (INpc Npc in client.Map.Npcs.Values) { if (Npc.MapID == 3935 && (Npc.UID == 29994) && Kernel.GetDistance(client.Entity.X, client.Entity.Y, Npc.X, Npc.Y) < 17) { Npc.SendSpawn(client); } } } if (client.Entity.InteractionInProgress && client.Entity.InteractionSet) { if (client.Entity.Body == 1003 || client.Entity.Body == 1004) { if (Kernel.GamePool.ContainsKey(client.Entity.InteractionWith)) { Client.GameClient ch = Kernel.GamePool[client.Entity.InteractionWith]; Data general = new Data(true); general.UID = ch.Entity.UID; general.wParam1 = new_X; general.wParam2 = new_Y; general.ID = 0x9c; ch.Send(general.ToArray()); ch.Entity.Action = Game.Enums.ConquerAction.Jump; ch.Entity.X = new_X; ch.Entity.Y = new_Y; ch.Entity.Facing = Kernel.GetAngle(ch.Entity.X, ch.Entity.Y, new_X, new_Y); ch.SendScreen(generalData, true); ch.Screen.Reload(general); client.SendScreen(generalData, true); client.Screen.Reload(general); } } } if (Kernel.GetDistance(client.Entity.X, client.Entity.Y, 73, 98) < 3 && client.Entity.MapID == 4020) //TOWEROFMYSTERY { client.Entity.Teleport(3998, 90, 352); } if (Kernel.GetDistance(client.Entity.X, client.Entity.Y, 40, 66) <= 1 && client.Entity.InTOM || Kernel.GetDistance(client.Entity.X, client.Entity.Y, 46, 44) <= 1 && client.Entity.InTOM) //TOWEROFMYSTERY { client.MessageBox("Do you want to leave the Tower of Mystery?", p => { p.Entity.Teleport(4020, 84, 74); }); } } else { client.Disconnect(); } } else { if (client.Entity.Mode == Game.Enums.Mode.None) { client.Entity.Teleport(client.Map.ID, client.Entity.X, client.Entity.Y); } } } else { if (Kernel.GetDistance(new_X, new_Y, client.Entity.X, client.Entity.Y) <= 17) { client.Entity.Action = Game.Enums.ConquerAction.Jump; client.Entity.Facing = Kernel.GetAngle(generalData.wParam1, generalData.wParam2, new_X, new_Y); client.Entity.X = new_X; client.Entity.Y = new_Y; client.SendScreen(generalData, true); client.Screen.Reload(generalData); } else { client.Disconnect(); } } I do know, for some reason when I add all my dmaps from my client into my server database for conversion folder "map/map/.dmap" that goes into "maps" folder for server to load as .map. It only converts like 236 dmaps, and not all of them. Misses maps like twin city for example. But I do have a backup of maps, which I merge. Just odd to me. What is If(Map.Floor[new_X, new_Y, Game.MapObjectType.Player, null])) doing? I presume checking if the new x and y are a valid space. breakpoint there, inspect map.floor and compare your coordinates. I don't know the structure but if you share more information maybe we can help you identify the exact problem. Others have pointed out the scene was not loaded properly. Basically there is a mismatch somehow between what your client percieves as navigatable terrain and what your server percieves as navigatable terrain. Map.Floor, and where it loads data in is what you should be tracking down.
-
It's what my server does. I make a folder in the database folder, and make sure the folder is called "map/map/blahblah.dmap", and it'll convert all of them with "id.map" and send them all to "maps" folder and yeah my server loads only from .map. I have saw some servers load directly from just dmaps. When you breakpoint it, what condition is causing it to fall through to that else?
-
Set a breakpoint on the invalid jump function (it should be in your MsgAction processing for jump). Follow the callstack to see what is triggering the kickback you are seeing when you jump to an invalid coordinate.
-
Excellent high quality release. Thank you for publicizing your work Cookie!
-
Potential positional D-Sync on mobs - client versus server
Rezlind replied to Rezlind's topic in Conquer Online
Wow that is fantastic, certainly better than what we came up with. Especially having them take the appearance of nearby monsters like meteor doves. Well played! I am excited to see your future project. -
Potential positional D-Sync on mobs - client versus server
Rezlind replied to Rezlind's topic in Conquer Online
Hey, I have a bunch of friends that play on your server but I hadn't heard them mention this before - just to be clear we weren't trying to rip off your idea. I'd love to see a clip of your loot goblin sometime. Also, we introduced a token that can be obtained from these "Rifts" or portal events and that can be used for some exclusive rewards, but conquer is notoriously difficult to produce good drop loot for because you don't really want to reward sockets too often or too early. What kind of rewards are your goblins dropping? Ours drop gold (of varying amounts), meteors and dragonballs in addition to the aforementioned rift tokens. -
Potential positional D-Sync on mobs - client versus server
Rezlind replied to Rezlind's topic in Conquer Online
Okay just for parity and to help anyone that may encounter similar frustrations in the future - This entire d-sync issue was faulty logic on my end. MsgAction (with Jump 137) has a deterministic end destination sent in the packet. The monsters original X Y and the destination X Y are sent in the same packet. However, with MsgWalk, all you supply is a direction this means it is your responsibility to ensure the monster direction is correct and that you update the coordinates to be exactly 1 pace difference on the server side. I made two mistakes: First, I was using the wrong direction variable as the monster AI had two modes (One when a player is in kill range) and one where the monster simply walks in the opposite direction of the player. (I was accidentally sending the direction it jumped when it was in kill range of a player which is not necessarily the same as the opposite direction of the player.) Secondly, I was sending the MsgWalk packet to the client regardless of whether the monster had actually moved due to an oversight on my part. There are instances where the monster could not conceivably go any further opposite of the player. (Loot goblins in Diablo behave similarly in that you can coral them into a wall to prevent their movement.) It was in those instances that if you send MsgWalk to the client it will display the monster traversing terrain it is not suppose to traverse. Anyway, tl;dr two fuck ups on my part and it is now behaving correctly. Spirited I actually tested the following as well: Here is what I found: MsgAction Action 137 -> Normal Jumps, with the animatiuon. MsgAction Action 130 -> "Guard Jump", instant teleportation no jump animation (At-least not for the monster model we were using.) MsgAction Action 156 -> "Ninja Step", a Normal jump whereby at the conclusion of the jump an "after image" of the monster was left behind. NinjaStep looked pretty cool but unfortunately did not fit the situation we were trying to use it in. Definitely keeping it in our back pocket for the future though. -
Potential positional D-Sync on mobs - client versus server
Rezlind replied to Rezlind's topic in Conquer Online
After further testing by adding map effects to better visualize where the NPC is suppose to be serverside it would appear that somehow in some instances the NPC was MsgWalking more than 1 coordinate which resulted in one of the situations where it would desync. -
Howdy, we're in the midst of implementing a feature whereby a special monster splits upon death similar to Diablo Loot goblins. Each new iteration after a split moves faster and farther away from the player than before. Below is a small gif demonstrating the behavior for some context: https://streamable.com/qf7qns (Don't mind the green artifacting - it happened during the gif encoding process.) One thing we've noticed is that it can be quite common for the position of the monster client side to be desynchronized from the actual position of the monster server side. We believe this may be related to sending a MsgWalk packet for the monster entity soon after or during a MsgAction Jump (137) packet for the monster entity. If that is the case - does anyone know of a way to ensure that positions do not become de-synced and or to re-synchronize the monster location? Alternatively, if we prevent MsgWalk packets from being sent for X ticks after a MsgJump packet - would that remedy the situation? Thanks for any advice!
-
Do you mean the resolution of the client?
-
It sounds like you might have your ports configured incorrectly. The account server runs on a port between 9958-9960 (depending on the patch). And then the game server always runs on 5816. Also, make sure you're connecting to the right realm. For example, if you configure your account server for Meteor, then you should connect to the Meteor server in the client. Thaaaanks, I got, the game is running and i got to login, but, there is no NPC, MAP and a some error packet. Maybe it is the database, where can i get the right data?? I have just comet.account (with 5 table) and comet.game (with 1 table) Thanks Spirited designed the skeleton/bones of what you would need. The rest of it is up to you to implement. That means reviewing packets and implementing them using the examples Spirited laid out in Comet.
-
My first instinct is you perhaps incorrectly configured your port for the game server? What port are you using for the game server right now?
-
Each source will have instructions on how to register an account (and/or require you to create an account manually in the database.) Pick a source from Spirited's server list and find it's appropriate client from the link Spirited provided then begin from there. Ok, so i found the 5615 Client but i didnt found the 5615 Source for that. Did i miss something or do i have to find it somewhere else ? cheers. I don't know that a source for 5615 exists. You would have to figure out how the login sequence works for 5615 and then upgrade a server source to match it, or find a higher source and downgrade it to match it. I would recommend looking at https://staging.cooldown.dev/topic/5-guide-server-downloads and selecting a version from the server side first, then finding it's respective client. There is a lot to learn and it would be better for you to start off with same version client to server by selecting the server source first.
-
Each source will have instructions on how to register an account (and/or require you to create an account manually in the database.) Pick a source from Spirited's server list and find it's appropriate client from the link Spirited provided then begin from there.
-
Yep. Just to add to what Rezlind said, that's more or less what I'm doing with my server as well. I develop on version 5615, but I don't plan to use a lot of the modern features. It's more or less just for stability and access to various new skills, items, bosses, and effects. If you want to disable a class the easy way (until you replace the Flash character creation screen), you can have the MsgRegister packet return an error like what I did here in Comet: https://gitlab.com/spirited/comet/-/blob/5187/src/Comet.Game/Packets/MsgRegister.cs#L80 Coincidentally the register invalid msg talk message solves one of the remaining tasks on our server project. Thanks
-
Yes, it is possible to downgrade the a later client to a lower version. It does involve becoming familiar with the login sequence changes that happened over time. Spirited has documented the login sequence changes for various versions. You can compare it by switching branches in the Gitlabs UI here: https://gitlab.com/spirited/comet/-/blob/5187/doc/LoginSequence.md