00:02 < bridge_> they are turned off anyway 👀 03:02 < ws-client> @jxsl13 one server can load one antibot module. Two servers can load one each :D is that what you meant? 03:03 < ws-client> and as jupstar pointed out you can also write code into the antibot module to load infinite amount of submodules but that code does not exist yet 03:03 < ws-client> i finished the thing seems to be working 05:28 < bridge_> @chillerdragon 05:28 < bridge_> > This process is a bit annoying. I have to uninstall half my OS to swap these. Because ffmpeg depends on sdl. 05:28 < bridge_> > Also I would like to use the one from pacman not the one from the AUR ._. 05:28 < bridge_> 05:28 < bridge_> `pacman -Rsndd sdl2-compat; pacman -Sdd sdl3` 07:29 < bridge_> Can you hot reload the antibob module? 07:29 < bridge_> ChillerDragon: 07:59 < bridge_> i meant your module is closed source and would contain the gctf patch. because it might be pointless to maintain two of those, one containing only the gctf patch, which I might get access to and one with your bob stuff. I could still manually patch the servers but yeah, would definitly be neat to use the plugin. 08:00 < bridge_> w/o the bob stuff 08:44 < bridge_> @robyt3 https://github.com/ddnet/ddnet/pull/10015#issuecomment-2788402095 08:44 < bridge_> 08:44 < bridge_> then what do 08:45 < bridge_> Remove the meta programming again I guess 08:45 < bridge_> so no enum class? 08:45 < bridge_> manually specifying the min and max is possible 08:46 < bridge_> Just cast to int? 08:46 < bridge_> oh right 08:46 < bridge_> that makes me sad tho xd 08:46 < bridge_> too bad 08:49 < bridge_> @robyt3 should i use underlying_type or just int 08:50 < bridge_> int 08:51 < bridge_> should i put the max enum value beside the definition 08:51 < bridge_> so when you add a protocl it doesnt have to be changed everywhere 08:52 < bridge_> ```cpp 08:52 < bridge_> enum class EProtocol 08:52 < bridge_> { 08:52 < bridge_> TW6_IPV6, 08:52 < bridge_> TW6_IPV4, 08:52 < bridge_> TW7_IPV6, 08:52 < bridge_> TW7_IPV4, 08:52 < bridge_> }; 08:52 < bridge_> ``` 08:52 < bridge_> ```cpp 08:52 < bridge_> static constexpr int s_ProtocolMax 08:52 < bridge_> ```? 08:52 < bridge_> ```cpp 08:52 < bridge_> enum class EProtocol 08:52 < bridge_> { 08:52 < bridge_> TW6_IPV6, 08:52 < bridge_> TW6_IPV4, 08:52 < bridge_> TW7_IPV6, 08:52 < bridge_> TW7_IPV4, 08:52 < bridge_> }; 08:52 < bridge_> ``` 08:52 < bridge_> ```cpp 08:52 < bridge_> static constexpr EProtocol s_ProtocolMax = TW7_IPV4; 08:53 < bridge_> ```? 08:56 < bridge_> Can't say I like this either. I don't know if we shouldn't just keep the NUM in (without meta-programming) 08:56 < bridge_> morning 08:56 < bridge_> heinrich doesnt like num cuz it means its a valid option 08:57 < bridge_> Not with the meta programming, but the NUM was already there 08:57 < bridge_> Post your suggestions on the PR so others can comment 08:57 < bridge_> https://cdn.discordapp.com/attachments/293493549758939136/1359422056198045806/image.png?ex=67f76bf3&is=67f61a73&hm=84009e3f778ceb316c59edc130115acea0158503c96769b3c8b5108a2c8b88e4& 08:58 < bridge_> mmk 08:59 < bridge_> like rust, less macros better, so less templates better imho 08:59 < bridge_> Use the latest entry instead of num maybe. Or just don't use C++ enums anymore in general but write our own enum class :justatest: 08:59 < bridge_> i was thinking that 09:00 < bridge_> but i was scared to get sent to the shadow realm for too much meta programming 09:00 < bridge_> Use the last entry instead of num maybe. Or just don't use C++ enums anymore in general but write our own enum class :justatest: 09:00 < bridge_> @ryozuki do you use a visitor trait I can yoink? 09:00 < bridge_> @ryozuki do you use a visitor trait so I can yoink it? 09:00 < bridge_> @ryozuki do you have a visitor trait so I can yoink it? 09:00 < bridge_> no i dont use visitor pattern 09:01 < bridge_> the lack of proper pattern matching in c++ makes dealing with enums need metaprogramming i guess, cenummap in rust would just be a hashmap with the enum as key 09:01 < bridge_> Well, it probably would, but at this point I don't see a clean alternative 09:02 < bridge_> in fact, why u need CEnumMap and not just std::unordered_map 09:02 < bridge_> well i guess cuz its a array internally 09:02 < bridge_> cpp doesnt do that 09:02 < bridge_> it just looks so ugly, u need to make a iterator and a map for every enum 09:02 < bridge_> i meant ur cenummap 09:03 < bridge_> oh right 09:03 < bridge_> no unright 09:03 < bridge_> std::unordered_map (the alternative) isnt just an array 09:03 < bridge_> for what its used for the performance cost doesnt matter 09:03 < bridge_> Depends on how much we care about the performance difference 09:04 < bridge_> `std::size_t Size = static_cast(Enum::NUM)` 09:04 < bridge_> this makes it ugly 09:04 < bridge_> sadly cpp doesnt have a way to get the max element 09:04 < bridge_> ggtg 09:05 < bridge_> cant u get the lass variant and add 1? 09:05 < bridge_> last 09:05 < bridge_> @sollybunny or just at declaration of CEnumMap pass the size of the enum, which the user can do by passing the last variant + 1 09:05 < bridge_> but i guess thats uglier 09:06 < bridge_> ok enums in cpp sux 09:11 < bridge_> you can skip the division by using known constants and multiplication instead. You can also look at PadĂ© approximants: https://en.wikipedia.org/wiki/Pad%C3%A9_approximant which are quite good but involves a true division 09:22 < bridge_> Programming against an api, User: "Why does feature X not work?" Me: "Wtf, why does this not work". Currently in a talk with the developers of API and confirmed a bug, their faces were priceless 09:43 < bridge_> classic 09:54 < bridge_> @robyt3 09:54 < bridge_> ```cpp 09:54 < bridge_> bool Ipv6 = m_aProtocolsEnabled[EProtocol::TW6_IPV6] || m_aProtocolsEnabled[EProtocol::TW7_IPV6]; 09:54 < bridge_> bool Ipv4 = m_aProtocolsEnabled[EProtocol::TW6_IPV4] || m_aProtocolsEnabled[EProtocol::TW7_IPV4]; 09:54 < bridge_> ``` 09:54 < bridge_> turns into a nightmare with static casts 09:55 < bridge_> @robyt3 09:55 < bridge_> ```cpp 09:55 < bridge_> bool Ipv6 = m_aProtocolsEnabled[EProtocol::TW6_IPV6] || m_aProtocolsEnabled[EProtocol::TW7_IPV6]; 09:55 < bridge_> bool Ipv4 = m_aProtocolsEnabled[EProtocol::TW6_IPV4] || m_aProtocolsEnabled[EProtocol::TW7_IPV4]; 09:55 < bridge_> ``` 09:55 < bridge_> even with c style its a bit wordy 10:05 < bridge_> @jupeyy_keks because you asked for a datastructure for maplayers: We could create a general Maplayer base class, and inherit buffered and unbuffered rendering from it, making the functions virtual that change. I don't know how "wanted" this change would be, as it would further devide the logic 10:05 < bridge_> Ironically enough glibc seems to not have anything that fancy. They use just range reduction and a taylor series approximation. Which kinda makes sense as close to 0 the taylor series approximation actually has less error and is more uniform 10:06 < bridge_> @jupeyy_keks because you asked for a datastructure for maplayers: We could create a general Maplayers (Rendering) base class, and inherit buffered and unbuffered rendering from it, making the functions virtual that change. I don't know how "wanted" this change would be, as it would further devide the logic 10:10 < bridge_> just noticed, that maplayers already is a virtualized class 😼 10:10 < bridge_> just noticed, that maplayers already is a virtualized class 😼 we don't have any derived classes, maybe an other dev had this already in mind 10:23 < bridge_> what about using some existing libs like something i sent last time 10:40 < bridge_> So many people working on just refactoring stuff now 10:47 < bridge_> Honestly the name "maplayers" to actually say rendering map already isn't really good. 10:47 < bridge_> I'd probably go with a very simple name "MapVisuals" or "MapRenderer". 10:47 < bridge_> 10:47 < bridge_> These map visuals contain a rendering pipeline. 10:47 < bridge_> This pipeline on highest level is: 10:47 < bridge_> - background 10:47 < bridge_> - physics 10:47 < bridge_> - foreground 10:47 < bridge_> 10:47 < bridge_> Then for background & foreground there is a list of layers (in order) to render. The type of layer is evaluated too (Quad or Tile layer). 10:47 < bridge_> Then simply render all layers like that. 10:47 < bridge_> 10:47 < bridge_> What I'd _NOT_ do is care about (map-)groups at all. 10:48 < bridge_> They are only used for the clipping and preparing the drawing canvas' projection attributes (offset, parallax). IMO this can also be done on a per layer basis. 10:48 < bridge_> 10:48 < bridge_> Now if you have a function called "RenderTileLayer" and this automatically splits into buffered and unbuffered is to me good enough over having yet another data structure to split this. (In a sense RenderTools already abstracts away the old rendering anyway for the editor rendering) 10:48 < bridge_> 10:48 < bridge_> To me it's mostly about making the flow of the rendering easier to follow. 10:49 < bridge_> You could also abstract most of these away: 10:49 < bridge_> https://github.com/ddnet/ddnet/pull/10043/files#diff-3139c99b7ed3b99518d6b597a2652b49938ec31306d96dbe8e92bba913d4da2aR1492-R1528 10:49 < bridge_> 10:49 < bridge_> The tile layer visuals simply have an attribute called overlays, in order. And these overlays by enum say which texture they want 10:51 < bridge_> Oh and I'd not make MapVisuals a client component anymore. 10:51 < bridge_> 10:51 < bridge_> You can have a client component "RenderCurrentMap". But that sounds unrelated to having a datastructure that can render maps 10:51 < bridge_> Then the background menu map would also be cleaner instead of using the client component 10:51 < bridge_> magic enums uses magic which i dont wnt to introduce 10:52 < bridge_> which is not magic 10:54 < bridge_> idk but when you code in other languages do you really check how every package u use is implemented 10:55 < bridge_> not really, but i have magic enums 10:55 < bridge_> and its compiler dependent magic 10:55 < bridge_> wdym 10:55 < bridge_> 10:55 < bridge_> i recommend you open this in vsc or something 10:56 < bridge_> oh yeah this is c++17 and over 10:57 < bridge_> @jupeyy_keks 10:57 < bridge_> > These map visuals contain a rendering pipeline. 10:57 < bridge_> > This pipeline on highest level is: 10:57 < bridge_> > background 10:57 < bridge_> > physics 10:57 < bridge_> > foreground 10:57 < bridge_> 10:57 < bridge_> I am aware of this, we have the MapLayers component twice in the gameclient, once for background and once for foreground (including entities). This is completly implicitly done in the component ordering 10:59 < bridge_> Could you help me abstract these away, you mean explicitly for the Buffered case, but we'd need to know what texture to set. I could also make the function get the overlay count and iterate over it and add the textures to set in an array or something like that 11:00 < bridge_> it's just a bit unclear to me how to do it _nicely_ 11:00 < bridge_> I'd make overlays a structure and have a vec of overlays that is iterated over 11:00 < bridge_> every overlay simply is of type centered_text, bottom_text or top_text 11:00 < bridge_> based on that you can get the texture 11:01 < bridge_> Additionally the set_texture call is similar. 11:02 < bridge_> 11:02 < bridge_> Make an enum 11:02 < bridge_> 11:02 < bridge_> Design(TextureIndex), 11:02 < bridge_> Game, 11:02 < bridge_> Front, 11:02 < bridge_> Tele, etc. 11:02 < bridge_> 11:02 < bridge_> Then you can almost completely remove that switch there 11:02 < bridge_> I mean it's not too important if you don't want to. It simply reduces the switch to where the actual difference is. 11:02 < bridge_> 11:02 < bridge_> In the end basically all tile layers, if design or physics are the same. They only differ in texture and overlays 11:03 < bridge_> I mean it's not too important if you don't want to. It simply reduces the switch to where the actual difference is. 11:03 < bridge_> 11:03 < bridge_> In the end basically all tile layers, design or physics, are the same. They only differ in texture and overlays 11:04 < bridge_> I can do that, no problem. I think it would be a lot cleaner, but wouldn't the switch logic just move into the Overlay creation? 11:04 < bridge_> Well at least it would be static đŸ€” 11:05 < bridge_> Yes, I guess for me it's more about, having a proper rendering structure. 11:05 < bridge_> 11:05 < bridge_> Some code is always somehow messy or needs lots of ifs etc. 11:05 < bridge_> I generally prefer it in initialization code 11:05 < bridge_> But again, if you prefer it otherwise do it as you like 11:05 < bridge_> This isn't really a strong opinion 11:06 < bridge_> I am sure there is some merit to it, but I'd do it in a followup 11:06 < bridge_> Doing this in small reviewable chunks is always a good idea, especially before introducing new features 11:13 < bridge_> Ryu(?) said that the world would be better if people stop added features and just did refactoring prs. I guess alot of people saw and acknowledged it. It also means people have a better understanding of the code which is good when old maintainers die and we need new ones 11:14 < bridge_> Yeah it is not bad. Just a curiosity, it used to be the opposite, people only were interested in doing new features 11:19 < bridge_> i'll just take that 11:19 < bridge_> i am very interested in doing new features 11:19 < bridge_> but in terms of gameplay ddnet is blocked behind being backwards compatible and waiting for tw 0.8 aswell as a way to do new tiles sustainably (have a testing phase, and that rfc thing) 11:19 < bridge_> in terms of client side things most new things take alot more effort atleast for me than refactoring, im also stuck on all the new features i want for ddnet and tclient 11:19 < bridge_> * i want to make the chat ui better for team/whispering 11:19 < bridge_> * i want to make new (0.7 style-ish) name tags and have them used everywhere instead of our very inconsistent nametags we have now 11:19 < bridge_> * i want chat bubbles for tclient 11:19 < bridge_> it uses binary search to determine number of enums xd 11:19 < bridge_> what 11:19 < bridge_> cuz you can see if an int value is in an enum 11:20 < bridge_> so you can binary search the underlying type 11:20 < bridge_> its jank as i said 11:20 < bridge_> anyway it also doesnt provide a map so the more meta programming is still needed 11:20 < bridge_> it does 11:20 < bridge_> tw 0.8 đŸ„Ž 11:20 < bridge_> do it? 11:20 < bridge_> i saw a pr about that 11:20 < bridge_> constant enum map stuff 11:21 < bridge_> https://github.com/Neargye/magic_enum/pull/187 11:21 < bridge_> its an excuse ive heard for new features 11:21 < bridge_> "ddnet is just a mod" 11:22 < bridge_> well cool then :D 11:23 < bridge_> https://github.com/ddnet/ddnet/pull/8341 11:23 < bridge_> i take this as a qol feature 11:23 < bridge_> instead of replacing the freezebar 11:23 < bridge_> https://github.com/Neargye/magic_enum/issues/150 11:24 < bridge_> this one also looks good but c++20 11:24 < bridge_> https://cdn.discordapp.com/attachments/293493549758939136/1359458941364474116/91581d1ad905911f.png?ex=67f78e4d&is=67f63ccd&hm=b280f0e18e9fbb7c26e5dff0c96d10de5dc56b8e36ce24b71ea61be00f1cb364& 11:24 < bridge_> it would be nice if we could stay cpp11 11:26 < bridge_> lmao 11:26 < bridge_> cpp11 is now considered old by this page 11:28 < bridge_> the reasons for me are: 11:28 < bridge_> 1. it feels cool to be able to compile in older versions 11:28 < bridge_> 2. niche compilers are often outdated, newer features are harder to convert (ranges eg) 11:28 < bridge_> 3. 3 11:28 < bridge_> if someone else pushes the min version to cpp17 or higher i wont complain 11:28 < bridge_> also llvm swallows alot of the niche compilers whole 11:28 < bridge_> (i think) 11:29 < bridge_> i cant name a third cpp compiler 11:29 < bridge_> i mean forth 11:29 < bridge_> fourth* 11:29 < bridge_> idk which one 11:29 < bridge_> clang, gcc, msvc, apple one 11:29 < bridge_> isnt apple just using clang 11:30 < bridge_> not sure, but i know they have something seperate 11:30 < bridge_> "Apple Clang*" 11:30 < bridge_> like on my macbook the default toolchain is clang 11:30 < bridge_> yes 11:30 < bridge_> https://cdn.discordapp.com/attachments/293493549758939136/1359460494162919536/image.png?ex=67f78fbf&is=67f63e3f&hm=2e3e72ae1e6f4dbdba4b9b78341cdef00d6a9fc982047c648b05a508dc11070f& 11:30 < bridge_> Hm, we might be on 17 nowadays tbf 11:30 < bridge_> by niche i meant consoles and things 11:30 < bridge_> for what? 11:31 < bridge_> we used nodiscard, fallthrough and maybe_unused attributes but those are ignored by cpp11 (or should be) 11:33 < bridge_> i cant see anythign else 11:33 < bridge_> but then again i havent looked at the whole codebase 11:33 < bridge_> amnt omnicient 11:34 < bridge_> std::clamp? or do we use our own 11:34 < bridge_> I don't remember now, but. I do remember seeing gnu++17 in the compile commands on mac 11:34 < bridge_> `using std::clamp;` in math.h 11:34 < bridge_> thats such a weak thing to cause cpp11 non compliancd xd 11:35 < bridge_> but pi isnt stolen from math.h? 11:35 < bridge_> i guess its impl dependent 11:36 < bridge_> Probably more than that idk 11:36 < bridge_> seems to be more precise (if that matters for a float) than anything i cna find 11:37 < bridge_> set: `3.1415926535897932384626433f` 11:37 < bridge_> real: `3.1415927410125732421875` 11:37 < bridge_> damn that bad 11:37 < bridge_> accurate to only 6dp 11:38 < bridge_> ```cpp 11:38 < bridge_> #include 11:38 < bridge_> #include 11:38 < bridge_> 11:38 < bridge_> int main() { 11:38 < bridge_> float pi = 3.1415926535897932384626433f; 11:38 < bridge_> std::cout << std::setprecision(30) << pi << std::endl; 11:38 < bridge_> return 0; 11:38 < bridge_> } 11:38 < bridge_> ``` 11:38 < bridge_> ^ professionally vibe coded 11:40 < bridge_> currently compiling with cpp11 to see if it works 11:41 < bridge_> no because im half way through doing something xd 11:41 < bridge_> You can also do a git blame on cmakelists to see who bumped it to 17 and when/why 11:41 < bridge_> its probably because of std::clamp 11:41 < bridge_> ill do that 11:42 < bridge_> Btw if we bumped at all, that means our oldest target has a c++17 compiler now 11:44 < bridge_> 11:45 < bridge_> https://github.com/ddnet/ddnet/pull/4709 11:45 < bridge_> it wasnt for any reason it seems 11:46 < bridge_> Seems it was for #4710 11:46 < bridge_> https://github.com/ddnet/ddnet/pull/4710 11:46 < bridge_> cant clang tidy version and cpp standard be differnet? 11:47 < bridge_> Wym? 11:47 < bridge_> Those modernize flags bring in stuff like `make_unique` which is C++14 and above e.g. 11:47 < bridge_> ```cpp 11:47 < bridge_> [192/192] Linking CXX executable DDNet-Server 11:47 < bridge_> [solly@solly buildcpp11]$ 11:47 < bridge_> ``` 11:47 < bridge_> std::make_unique? i thought that was ages ago 11:48 < bridge_> huh 11:48 < bridge_> no way we are sticking with cpp11 then 11:48 < bridge_> probably ddint set flags right 11:49 < bridge_> anyway maintainers dont have the same urge to use the smallest version number standard 11:51 < bridge_> Well our metric is what the oldest debian we build on supports 11:51 < bridge_> i see 12:51 < bridge_> wait there is std::clamp :jusa 12:51 < bridge_> wait there is std::clamp :justatest: 12:53 < bridge_> why is clamp(v, lo, hi) and not (lo, v, hi), this always bothers me 12:56 < bridge_> why is it not v.clamp(lo, hi) :) 12:57 < bridge_> [nearly nothing]() in the codebase uses std::clamp, but [nearly everything]() the self made `clamp` 12:57 < bridge_> 12:57 < bridge_> @chillerdragon we have that std:: rule, clamp might be the exception. 14:14 < bridge_> which language has clamp(min, x, max)? that's insane 14:35 < bridge_> Hot reload is always cool. But as of right now I do not need it in the antibot. I spent so much time on making hot reload work how I want it to work in twbl probably not repeating that for antibot. I rather build a plugin system that has a more feature rich interface than antibot which can do hotreloading 14:36 < bridge_> @jxsl13: the idea is that it now just works. No more maintenance needed from either of us. If for some reason you want to keep updating it we will find a way 14:49 < bridge_> will you provide like access to the compiled module or something like that? 14:49 < bridge_> a plugin system with a rich interface would be really nice, the antibot ABI is barely functional for anything other than heuristic based antibots 14:50 < bridge_> or have a secondary repo for tue gctf stuff only? 14:50 < bridge_> the 14:51 < bridge_> my question is basically, how and to what do I get access. 14:52 < bridge_> If you are missing something feel free to PR it. We do a lot more than heuristics in our module 14:53 < bridge_> The ability to intercept all packets and send packets pretty much allows for anything you'd like 14:53 < bridge_> I want the gamestate in it's entirety so I can do prediction based heuristics 14:54 < bridge_> the "self made" is just a reference to std::clamp in base/math.h, we should always use std::clamp i think 14:54 < bridge_> except modify the gamestate, I would also like the ability to change anything about the gamestate from the antibot 14:55 < bridge_> I see what you want, yeah that's not the kind of thing that's easy with the current way the antibot works. I doubt we'd be open to patches that tie the antibot that deeply into the gamestate either 14:55 < bridge_> So yeah something like a plugin system would be more interesting for your usecase 15:01 < bridge_> wizardry https://github.com/facet-rs/facet 15:01 < bridge_> https://docs.rs/unsynn/0.0.26/unsynn/ 15:01 < bridge_> is it compiletime reflection? 15:01 < bridge_> I guess we need a PR to fix issues like this if we ant to inline that 15:02 < bridge_> or get rid of this maximum/minimum/clamp functions 15:03 < bridge_> > The Poke type is able to allocate (or work from a &mut MaybeUninit) any Facet type, and gradually initialize its fields — until the fully-built value is moved out of the partial. 15:03 < bridge_> This sounds turbopog 15:03 < bridge_> its a dark magic crate 15:04 < bridge_> https://docs.rs/facet/0.1.4/facet/hacking/index.html 15:04 < bridge_> > The Facet trait is the cornerstone of our reflection system. It provides a way to access type information at both compile time and runtime, enabling powerful meta-programming capabilities while maintaining Rust’s safety guarantees. 15:04 < bridge_> https://cdn.discordapp.com/attachments/293493549758939136/1359514333159227603/image.png?ex=67f7c1e3&is=67f67063&hm=5efb0a4f0804d18897566200014640d99601fba57969f5d3224fdc239c473a9a& 15:04 < bridge_> https://cdn.discordapp.com/attachments/293493549758939136/1359514403061497876/image.png?ex=67f7c1f4&is=67f67074&hm=8bb920e0ddd455a32addbe3aaa5f313d0c7f387341963bf0f11666009beb5773& 15:48 < bridge_> clamp is probably used with `using` because there would have been too many changes to add `std::` everywhere 15:56 < bridge_> ы 16:14 < bridge_> Is that so bad? 16:14 < bridge_> Also thoughts on using better enums (Google it) 16:15 < bridge_> Looks like it does everything we want at the cost of it's macro magic 16:28 < bridge_> looks just as much compiler dependent magic as ... 16:59 < bridge_> https://cdn.discordapp.com/attachments/293493549758939136/1359543183595540631/image.png?ex=67f7dcc2&is=67f68b42&hm=24cb9262c974c06b554ea2991bd99147d5e03b823048944cead25c2179529624& 16:59 < bridge_> i havent looked at the implementation yet 17:07 < bridge_> its horrid 17:15 < bridge_> i would just blackbox it whatever 17:39 < bridge_> lol what’d syn do wrong 17:42 < bridge_> slow compile time afaik 18:07 < bridge_> We shouldn't have used an uuid for teehistorian files (and game ids). Now that I'm doing some things with them a snowflake would have been much more useful, or something else that gave a bit of ordering, even if they were k-sorted that'd be more useful 18:08 < bridge_> or something like instagram ids 18:12 < bridge_> authoritative serial type 😎 18:14 < bridge_> (btw just the index file for teehistorians is 3.3 gigs now, that's 22.6 million teehistorian files that are indexed 18:14 < bridge_> (btw just the index file for teehistorians is 3.3 gigs now, that's 22.6 million teehistorian files that are indexed) 18:14 < bridge_> curious how much of that (in bytes) is UUIDs 18:15 < bridge_> `data/2018-halloween/bra/0b56925e-d2e4-4e75-9c2e-7bb8f37c41c7.teehistorian,0b56925e-d2e4-4e75-9c2e-7bb8f37c41c7,2018-11-01T16:50:42+01:00,8362,Kobra,6f4b0bab,598947` 18:15 < bridge_> These are what the lines look like 18:18 < bridge_> 72 characters for the uuid for the 2 times it occurs there, times 22.6 million, that's 1.6 gigabytes of the file just having the uuids 😄 18:18 < bridge_> (it doesn't matter, that's the point of an index, it keeps data that is redundant, my issue with the uuids is that they are not sortable at all) 18:20 < bridge_> yeah 18:20 < bridge_> lmao 18:36 < bridge_> well, single precision float has only 6 to 7 digits precision, so 3.141592 has 7 digits and whatever is after doesn't matter 18:38 < bridge_> https://github.com/rust-lang/rust/issues/21102 this is so annoying when working with rust 18:38 < bridge_> I keep having to remember to set a breakpoint on `rust_panic` 18:41 < bridge_> https://ntietz.com/blog/til-uses-for-the-different-uuid-versions/ 18:43 < bridge_> A v7 uuid might have been interesting here I guess 18:45 < bridge_> WTFF 😭😭 https://www.youtude.net/watch?v=i50wel2lVsw 18:46 < bridge_> ban you? 18:53 < bridge_> ChillerDragon you had a teeworlds/ddnet packet decoder somewhere, where? 19:10 < bridge_> ``` 19:10 < bridge_> 2564893 ConsoleCommand { cid: 1, flag_mask: 128, cmd: "timeout", args: ["timeoutcodehere"] } 19:10 < bridge_> 2564893 Drop { cid: 1, reason: "Timeout Protection used" } 19:10 < bridge_> 2564893 ConsoleCommand { cid: 1, flag_mask: 128, cmd: "emote", args: ["angry", "999999"] } 19:10 < bridge_> ``` 19:10 < bridge_> 19:10 < bridge_> I was wondering why I was getting a crash, turns out a client that has gotten dropped can use console commands 19:10 < bridge_> @patiga do you think this is unintended or normal? Worth creating an issue for? Fixable? 😄 19:11 < bridge_> it crashes doesn’t it 19:12 < bridge_> is this a crash in the replayer, or where? 19:12 < bridge_> Not only can they use console commands but they also still get logged by teehistorian `2564895 PlayerOld { cid: 1, pos: (336, 625) }`, this one is still before the join at `2614778` 19:13 < bridge_> It crashes some custom tooling I had that assumes you can't do anything before a `Join` and after a `Drop` 19:13 < bridge_> would've considered that a sound assumption 19:13 < bridge_> is this important for some timeout stuff? 19:14 < bridge_> Timeout `Drop`s seem to be the only thing that violates that assumption 19:14 < bridge_> what’s join? join as in “entered and joined the game” or as in, connected? 19:14 < bridge_> wouldn’t make sense that way actually 19:14 < bridge_> Thankfully doesn't really matter at all for my use, I can just drop the messages that happen inbetween and I won't lose anything 19:15 < bridge_> @zwelf2 weird teehistorian stuff, please share your knowledge :owo: 19:16 < bridge_> Join as in just established a proper connection, it's logged at the end of `CServer::NewClientCallback` 19:18 < bridge_> Perhaps timeout rejoins should be logged as a `teehistorian-rejoinver6@ddnet.org`, that's what they seem to behave more like 19:19 < bridge_> Anyway, only @heinrich5991 and @zwelf2 would possibly know the intricacies of this one if anyone knows it at all 😄 19:26 < bridge_> Doesn't look great, it means including 1300 LOC in almost every file, the effect on compile time alone already seems non-negligible. It also adds a lot of stuff we don't need which only bloats compile-time/link-time or the binary, like converting enums to and from strings 19:27 < bridge_> yeah ): 19:27 < bridge_> cant that reflection cpp14 thing be added 19:27 < bridge_> to the spec 19:27 < bridge_> pretty plz mx cpp people 19:28 < bridge_> will only be in cpp38 x-x 19:28 < bridge_> i dont think u could swing it 19:29 < bridge_> ddnet’s too old to bring in utility libraries like that without great reason 19:29 < bridge_> but its like 19:29 < bridge_> argh 19:29 < bridge_> i just want to write clean code, please give me the tools 19:29 < bridge_> isnt cpp supposed to be the "heres everything, go do whatever" lang 19:30 < bridge_> not really 19:30 < bridge_> dependencies are much bigger liabilities in cpp compared to like, any other language 19:30 < bridge_> for a few reasons 19:31 < bridge_> im talking about cpp itself, not any libraries 19:31 < bridge_> what proposal are u talking about 19:31 < bridge_> holdon lemme find it 19:32 < bridge_> https://open-std.org/JTC1/SC22/WG21/docs/papers/2023/n4950.pdf#page=31 19:32 < bridge_> i belive its this 19:33 < bridge_> nvm 19:35 < bridge_> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4428.pdf 19:35 < bridge_> What are you guys doing with enums? I'm scared of the things you are talking about 19:35 < bridge_> its this and its cpp17 19:35 < bridge_> fancy reflective things afaik 19:35 < bridge_> clean enum maps and iteration 19:35 < bridge_> 200x slower at runtime 19:35 < bridge_> awwww yeah!! 19:35 < bridge_> nu-uh 19:35 < bridge_> 200x slower at compile time actually 19:35 < bridge_> just compile time yeah 19:35 < bridge_> Runtime should be exactly the same 😄 19:35 < bridge_> i dont want to use casts or macros or custom classes 19:36 < bridge_> i dont see why you cant get the values in an enum as a constexpr vec ): 19:37 < bridge_> u could for sure! 19:37 < bridge_> it’d take a macro 19:38 < bridge_> yeah and thats not happening 19:38 < bridge_> https://github.com/aantron/better-enums/blob/master/enum.h :pepeW: 19:38 < bridge_> as seen by robys [PepeHands~1](https://cdn.discordapp.com/emojis/606569496940904464.webp?size=48&name=PepeHands%7E1) 19:38 < bridge_> mhm. i see. 19:38 < bridge_> https://cdn.discordapp.com/attachments/293493549758939136/1359583245494128783/IMG_7971.png?ex=67f80211&is=67f6b091&hm=0c086eba176fb790002fdd2cfc37433600767c0ec3c8b5ec1f6f25ba412c7db6& 19:38 < bridge_> oh thats just compiler compliance nonsense 19:38 < bridge_> thats not the bad part 19:39 < bridge_> i really need a bath 19:39 < bridge_> then i eep 19:39 < bridge_> goodnight people and melon 19:39 < bridge_> im not really reading the code here 19:39 < bridge_> but like 19:39 < bridge_> is this all just to get strings bound to enum constants? 19:40 < bridge_> no 19:40 < bridge_> . 19:40 < bridge_> We just want the enum count with it being a literal 19:40 < bridge_> without casts or extra literal for num 19:40 < bridge_> We just want the enum count without it being a literal 19:40 < bridge_> Found another unexpected thing about the teeworlds protocol. `ClStartInfo` can happen twice, caused by rejoins again 19:41 < bridge_> And why is it being a literal an issue? 19:41 < bridge_> Leads to logic errors if the NUM literal is used as a value 19:41 < bridge_> how so 19:42 < bridge_> And IDEs would always want to add this case in switch statements 19:42 < bridge_> https://cdn.discordapp.com/attachments/293493549758939136/1359584158380327104/image.png?ex=67f802eb&is=67f6b16b&hm=2f4cd2ffb7683ceae95ef7fb7de5dc5d3e4c08fe13f1aee2889b44fc3ada96de& 19:42 < bridge_> youtube gives me weird recommendations 19:42 < bridge_> yeah i don’t like programming content at all 19:42 < bridge_> the best word i can use to describe most ppl who make it can’t be used here 19:42 < bridge_> Someone could assign the NUM literal value to a variable of the enum type but other code only handles the real enum literals 19:43 < bridge_> this is determined by whether the enum is an enum class or not, isn’t it 19:44 < bridge_> or are we talking about weird lvalue/rvalue stuff 19:45 < bridge_> i haven’t seen the code in question or the use case 19:45 < bridge_> `enum class`es are safer because they do not implicitly convert to other `enum` and `int` types 19:46 < bridge_> Honestly what is even the expected outcome of `EMyEnum::size()` for something like this? 19:46 < bridge_> ```cpp 19:46 < bridge_> enum EMyEnum { 19:46 < bridge_> CAKE=-1, 19:46 < bridge_> FOO=10, 19:46 < bridge_> CHEESE=2, 19:46 < bridge_> BAR, 19:46 < bridge_> HELLO, 19:46 < bridge_> WORLD=-1, 19:46 < bridge_> } 19:46 < bridge_> ``` 19:47 < bridge_> That should be an illegal use of `enum` IMO 19:47 < bridge_> I'd wager that's why this isn't really a thing in the standard, it would take 3 decades for the working group to agree on any definition for it 😄 19:47 < bridge_> If you want arbitrary values then use `static constexpr` 19:48 < bridge_> Only valid use of `enum class` (unless it replaces legacy code) should be without specifying any literal value explicitly 19:49 < bridge_> They should have just copied Java's enums TBH 19:53 < bridge_> this is a good video 19:54 < bridge_> Things like 19:54 < bridge_> ```cpp 19:54 < bridge_> enum EProtocol { 19:54 < bridge_> ALL=-1, 19:54 < bridge_> V6, 19:54 < bridge_> V7, 19:54 < bridge_> DDNET, 19:54 < bridge_> } 19:54 < bridge_> ``` 19:54 < bridge_> are pretty useful IMO 19:57 < bridge_> Golang enums with iotas are also cool 19:57 < bridge_> ```go 19:57 < bridge_> const ( 19:57 < bridge_> FLAG_PHYS1 = 1 << iota 19:57 < bridge_> FLAG_PHYS2 19:57 < bridge_> FLAG_PHYS3 19:57 < bridge_> ) 19:57 < bridge_> ``` 19:59 < bridge_> This is actually an awesome video you should watch it 19:59 < bridge_> ok#se 19:59 < bridge_> okĂ€se 20:06 < bridge_> You could have gotten that without implicitly allowing enums to be assigned a numeric value. I would have made enums more like classes so you could implement `int value() const` however you like if you want enum literals to correspond to other values. 20:06 < bridge_> You could have gotten that without directly allowing enums to be assigned a numeric value. I would have made enums more like classes so you could implement `int value() const` however you like if you want enum literals to correspond to other values. 20:07 < bridge_> LOL I looked at how `magic_enum` does it out of curiosity. They go from -128 to 128, get the compiler to evaluate a template function containing the value using `__PRETTY_FUNCTION__`, then they see if the number became a name 20:07 < bridge_> If you add this to our codebase I'll actually cry 20:11 < bridge_> Non unique elements should be treated as alias for the purpose of size if there were one. I am more interested in the spec adding ways to do common enum things, that is to iterate through all (unique) values and map all enum values to a templated type 20:11 < bridge_> I hope we do get some static reflection in the standard soon-ish 20:12 < bridge_> The enum traits thing that I linked doesn't implement what I want, but it makes it trivial to have classes which do without any jank 20:13 < bridge_> Also I wouldn't be completely opposed to code generation, for each enum type we could generate at compile time a metadata struct template specialization. But I doubt that plays better with yalls IDEs 20:13 < bridge_> Code gen is horrid 20:13 < bridge_> I disagree, codegen is the pinnacle of metaprogramming 20:14 < bridge_> Have you seen the codegen in DDNet 20:14 < bridge_> I've tried twice to make it more readable 20:14 < bridge_> What is horrid is at compile time trying every value between -128 and 128 and invoking the compilers template evaluation using a non-standard macro 😛 20:14 < bridge_> And I then cried 20:14 < bridge_> I'm not saying magic enums or better enums is a good solution 20:14 < bridge_> Just because DDNet doesn't do it great and you failed to clean it up doesn't mean all code generation is bad 20:14 < bridge_> Mmmm 20:15 < bridge_> But you're writing strings to a file 20:15 < bridge_> Most of the jank in ddnets code generation is stuff we inherited from teeworlds 20:15 < bridge_> i guess this explains your taste in memes too xd 20:15 < bridge_> It is actually a good video 20:16 < bridge_> i’m sure they explain things 20:16 < bridge_> Well yeah that's what code generation is, no? Generating code 20:16 < bridge_> The solution that I think will raise the least eyebrows is having NUM as a separate constant 20:16 < bridge_> agreed but my boss doesn’t think so 20:16 < bridge_> No Syntax highlighting, errors and other ide shenanigans before a compile 20:17 < bridge_> No, people will forget to update them. Just keep it in the struct as we've been doing since 1972 20:17 < bridge_> No, people will forget to update them. Just keep it in the enum as we've been doing since 1972 20:17 < bridge_> we generated some code from an API doc & his qualm is not that the doc is wrong, but our types are wrong (
as a result of the former) 20:17 < bridge_> The constant will be right next to the enum 20:17 < bridge_> but he just wants to renounce code gen altogether 20:18 < bridge_> I wrote something like this while I was working, it had a full type translator built in and configurable exceptions 20:18 < bridge_> nice 20:18 < bridge_> there are a few rust ones 20:18 < bridge_> And it'll be forgotten right next to the enum 20:18 < bridge_> the one i’m using is mostly fine but i definitely have to so some build.rs find & replace 😬 20:18 < bridge_> I know most people are blind but I know roby isn't and so aren't alot of maintainers 20:19 < bridge_> it wouldn’t be easier if we made the types ourself either, he really just wants me to use the serde json pointer instead to traverse as if it was a JS object 20:19 < bridge_> web dev pilled 20:19 < bridge_> i wanna die 20:19 < bridge_> I mean feel free to make the change, but I'll -1 it. I don't see the value in a change like that just to silence your IDE 20:19 < bridge_> the one i’m using is mostly fine but i definitely have to do some build.rs find & replace 😬 20:19 < bridge_> For the enum thing it's for type safety 20:20 < bridge_> Me not liking codegen is a separate thing 20:20 < bridge_> And I'm not really ever going to touch it 20:20 < bridge_> We are having 3 convos at once 20:21 < bridge_> why though? You cannot have always sequential values or flags 20:22 < bridge_> I moved on from the codegen convo anyway, I'm saying a separate constant for every enum just so your IDEs are happy are kinda a no go for me 20:22 < bridge_> why though? You cannot always have sequential values or flags 20:23 < bridge_> You can in my design of enums. Consider enums as classes instead, then implement `int value() const { return 1 << ordinal();}`, with `int ordinal()` being a standard function for all enum types 20:24 < bridge_> ```cpp 20:24 < bridge_> enum EMyEnum { 20:24 < bridge_> FOO, 20:24 < bridge_> BAR 20:24 < bridge_> }; 20:24 < bridge_> static constexpr std::underlying_type_t s_Count = BAR + 1; 20:24 < bridge_> ``` 20:24 < bridge_> This not only looks nasty, but is also something that is 100000% going to get missed when adding something and cause someone to spend some good amount of time in gdb 20:24 < bridge_> For me an `enum` is an enumeration, not a group of related values 20:24 < bridge_> unrelated. sorry for potential derailment. can (whoever maintains the DDNet bot) take off the /points cooldown if no data was actually generated?? i just input the wrong username the first time and now i gotta wait -_- 20:25 < bridge_> @murpi :this: ? 20:25 < bridge_> thx 20:25 < bridge_> & yea 30 sec is no huge deal but 20:25 < bridge_> it’s a little annoying 20:27 < bridge_> has always been used as a group of related values though. How about enumeration in non standard arithmetic group (like GF(256)) 20:27 < bridge_> what was so bad about the regular enums that we need an enum library? 20:28 < bridge_> Did y'all see `std::source_location` in C++20 btw, pretty cool replacement for `__LINE__` and `__FILE__` 20:28 < bridge_> https://discord.com/channels/252358080522747904/293493549758939136/1359584050498502877 and the message 2 below 20:29 < bridge_> it will finally be used in 20 years :poggers: 20:31 < bridge_> i think ppl would need an example of how it’d be used to get behind the enum thing 20:31 < bridge_> not even to support it strictly just to understand the purpose 20:31 < bridge_> i still don’t 20:34 < bridge_> This is what @sollybunny 's last suggestion would look like, so whenever you update an enum you need to update the associated count variable 20:35 < bridge_> With a library that does nasty compile time magic there'd be something like `magic_enum::count` that keeps track of that 20:35 < bridge_> you don’t just have count as the last enum item? 20:36 < bridge_> that’s what i do, assuming they’re sequential 20:36 < bridge_> no, that's what they don't want, since it's technically not an actual item of the enum it confuses ides 20:36 < bridge_> :/ 20:36 < bridge_> i see 20:36 < bridge_> who are these IDEs in question? 20:36 < bridge_> I don't have this issue 20:37 < bridge_> idk I'd open an issue at the vendor since this is a trick that has been around since the westward expansion of the united states 20:37 < bridge_> are we talking about static analyzers in situations where enum is being used in a switch or something 20:38 < bridge_> i wonder how that confusion even manifests in the IDE 20:38 < bridge_> Robyt3 mentioned IDEs try to add a case for it in a switch 20:40 < bridge_> default: std::unreachable(); 20:41 < bridge_> the cpp conundrum 20:41 < bridge_> TIL std::unreachable 20:41 < bridge_> writing safer/explicit code is too extra for ppl 20:42 < bridge_> oh this is cpp23 20:42 < bridge_> yea 20:43 < bridge_> Undefined behavior by design :monkaS: 20:43 < bridge_> well they have no means of restricting UB so they just consider it no man’s land 20:43 < bridge_> We already have this anyway, just without undefined behavior in release builds: 20:43 < bridge_> ```cpp 20:43 < bridge_> #if defined(__cplusplus) 20:43 < bridge_> [[noreturn]] 20:43 < bridge_> #endif 20:43 < bridge_> void 20:43 < bridge_> dbg_break(); 20:43 < bridge_> ``` 20:44 < bridge_> in cpp26 there will be “erroneous behavior” where guaranteed dangerous/invalid behavior is shut down asap 20:44 < bridge_> at compile time 20:44 < bridge_> They could have done this safely and implemented it as an assertion failure 20:44 < bridge_> I guess the difference is that it doesn't allow optimization the same way 20:44 < bridge_> [[indeterminate]] :brownbear: 20:44 < bridge_> we need more attributes 20:45 < bridge_> the point is so the compiler can remove that branch 20:45 < bridge_> if the assert is there it can't remove it? 20:45 < bridge_> the UB invoked by `std::unreachable` allows the compiler to aggressively assume that case can never happen without omiting the case 20:45 < bridge_> yeah, but now you just get a bug if the branch would have been taken 20:45 < bridge_> the `dbg_assert` in our default branches have already triggered several times 20:46 < bridge_> the `dbg_assert`s in our default branches have already triggered several times 20:46 < bridge_> well the bug is accepted for the optimization 20:46 < bridge_> the branch can’t be taken if optimized correctly 20:46 < bridge_> Depends on your goal I guess, if something is actually performance critical you don't want to branch all the time to check a case that can't actually possibly happen 20:46 < bridge_> It would be nice if it was all type safe and we didn't need dbg asserts 20:46 < bridge_> Yeah the branch predictor helps but what if you were working on an embedded device with not so smart of a branch prediction unit, or none at all? 20:47 < bridge_> i think it’s fairly synonymous with the assume family of attributes which directly tell the compiler “just act as if things were this way, trust me” 20:47 < bridge_> i hope you’d get other errors before u see it at runtime 20:47 < bridge_> I'm a huge fan of being unsafe, so take what I say with a grain of salt 20:50 < bridge_> this is an impressively good footgun, because I think if you put it as the default case it could cause the control flow to just go somewhere if you give the switch a value that isn't specified. 20:51 < bridge_> CVE generator 20:51 < bridge_> CVE generator 9000 20:51 < bridge_> uhh 20:51 < bridge_> this branch will not exist in optimized builds 20:51 < bridge_> it’s all elaborate hinting to the compiler which enables it to be optimized out 20:51 < bridge_> and trapped in debug builds 20:52 < bridge_> yes but the compiler can make a lookup table indexed in your switch value, and then you can pass it a value that's out of bounds and yeet the control flow 20:53 < bridge_> and this couldn’t happen before? 20:53 < bridge_> you’d need to be in the position to pass arbitrary data 20:53 < bridge_> past what the compiler expects 20:54 < bridge_> default case is supposed to handle all values, but if you tell it that the default is "unreachable" then the compiler can assume that you will only give it values that made cases for 20:54 < bridge_> right 20:54 < bridge_> default case is supposed to handle all values, but if you tell it that the default is "unreachable" then the compiler can assume that you will only give it values that you made cases for 20:54 < bridge_> i dont have a good cpp env handy at all but i think you’d need to start with a vulnerability to make anything of this 20:55 < bridge_> or just pass it arbitrary data to start with which would be bad 20:55 < bridge_> well yeah 20:55 < bridge_> bcs you always need to handle the default case 20:55 < bridge_> if you’re working with an enum or fixed/known shit at compile time.. i think there’s no issue 20:57 < bridge_> youd have to see what the compilers actually do 20:57 < bridge_> idk why you'd even put the default case if you say it's unreachable 20:58 < bridge_> the alternative is that it goes unchecked or you do something to it yourself.. both of which are often valid cases, you just need to know what you’re after 20:59 < bridge_> you’d put unreachable down if you had maybe an integral coerced from an enum, known invalid/impossible/deprecated enum values (very common), etc 21:00 < bridge_> is it better than doing your own error if it was to happen at runtime? no. does it enable the compiler to see into that possibility and prevent it in the first place? yes 21:01 < bridge_> is that how it works rn? also most likely no 21:01 < bridge_> I have no idea what you're talking about anymore 21:01 < bridge_> modern cpp developments are built on wishful thinking 21:01 < bridge_> idk if i do 21:01 < bridge_> im subconsciously avoiding something else 21:01 < bridge_> so i just be typing 21:01 < bridge_> it be like that 21:04 < bridge_> ```cpp 21:04 < bridge_> auto y; 21:04 < bridge_> switch(x) { 21:04 < bridge_> SOMETHING: 21:04 < bridge_> y = something; 21:04 < bridge_> } 21:04 < bridge_> // if x isn’t SOMETHING, y goes uninitialized 21:04 < bridge_> ``` 21:04 < bridge_> 21:04 < bridge_> ```cpp 21:04 < bridge_> auto y; 21:04 < bridge_> switch(x) { 21:04 < bridge_> SOMETHING: 21:04 < bridge_> y = something; 21:04 < bridge_> default: 21:04 < bridge_> std::unreachable(); 21:05 < bridge_> } 21:05 < bridge_> // if x isn’t SOMETHING, the scenario where y goes uninitialized is “handled” with no extra work 21:05 < bridge_> ``` 21:05 < bridge_> if you wrote a default case & did something else with it, that’d be the “safest”/most explicit option. i guarantee most ppl don’t have time to do that for every enum 21:05 < bridge_> I'm pretty sure in the second case if x isn't something you've UBed 21:06 < bridge_> this is pseudo code anyway but.. yeah 21:06 < bridge_> you do reach UB 21:06 < bridge_> std unreachable is the point 21:07 < bridge_> the idea is that the presence of the symbol indicates to the compiler that, if at any point in its speculative/eager evaluation of possible branches, if you reach the unreachable call, that path is guaranteed to be optimized out 21:07 < bridge_> hopefully enabling a compile time error instead of UB 21:07 < bridge_> compilers also warn if you don’t cover every switch case. but yknow 21:08 < bridge_> idk what the point you're trying to make is, I don't disagree with anything you've said 21:09 < bridge_> :kek: 21:09 < bridge_> that doesn’t count as disagreement 21:10 < bridge_> i just thought there was uncertainty about what i was saying 21:10 < bridge_> so i was hoping to clarify 21:10 < bridge_> if you don't want anything to happen but default just don't put the default and accept your tiny performance penalty xd 21:10 < bridge_> if you don't want anything to happen by default just don't put the default and accept your tiny performance penalty xd 21:10 < bridge_> https://en.cppreference.com/w/cpp/utility/unreachable "invokes undefined behavior" 21:10 < bridge_> who would want that though ? 21:11 < bridge_> doesn't mean it could crash 21:11 < bridge_> That `std::unreachable()` there is literally telling the compiler `x == SOMETHING` go ham 21:11 < bridge_> worse, it could not crash 21:11 < bridge_> UB may not trap or crash (like integer overflow) 21:11 < bridge_> It's not the undefined behaviour you desire, it's the more aggressive optimization that enables that you desire 21:11 < bridge_> https://izaron.github.io/posts/std-unreachable/ 21:12 < bridge_> then just don't add the `default: ` in there 21:12 < bridge_> the same assumptions can’t be made 21:12 < bridge_> back compat 21:12 < bridge_> this saves 1 branch, that's the purpose 21:12 < bridge_> in exchange you get scary UB 21:13 < bridge_> That doesn't convey the same information to the compiler though. Omiting a default branch is saying something should only happen if one of the cases gets passed. Explicitly marking a default branch unreachable is saying that nothing but those cases will ever be passed 21:13 < bridge_> yea 21:14 < bridge_> if you were to mandate the default: branch, you’d put [[fallthrough]]; to annotate it behaves as if the default wasn’t there 21:14 < bridge_> if you were to mandate the default: branch, you’d put [[fallthrough]]; to annotate that it behaves as if the default wasn’t there 21:15 < bridge_> This demonstrates what it's useful for rather well 21:16 < bridge_> it is really funny how it's defined as "invokes undefined behavior" 21:16 < bridge_> (this is all before a useless discussion on whether space age hardware needs such optimization and considering the fact that your toaster can run doom, some people need it, this enables it, done) 21:16 < bridge_> (or leave it empty, it’ll warn tho) 21:18 < bridge_> It's nice that rust enforces all possible branches of a match statement to be typed out, but sadly the rust `unreachable!` is useless, you have to use `unreachable_unchecked!` to get the same benefit 21:19 < bridge_> and that’s unsafe isn’t it 21:20 < bridge_> Yep, so it's a lot of typing, designed to discourage you from knowing what you are doing 21:21 < bridge_> I should try writing rust in a massive unsafe block sometime 21:21 < bridge_> agreed 21:21 < bridge_> does the borrow checker completely ignore you if you put unsafe around every line of code? 21:23 < bridge_> more than the borrow checker 21:23 < bridge_> i think it would warn you of anything it’d find to be unsafe normally 21:23 < bridge_> but the reason you’d do it is to use raw pointers and stuff, avoiding all that anyway 21:23 < bridge_> erm 21:23 < bridge_> https://cdn.discordapp.com/attachments/293493549758939136/1359609762529939466/image.png?ex=67f81ac3&is=67f6c943&hm=cb0f3dfa618c9109d4d5c58c3e7952239ec8d73e7371a9d1c3f238bab39603cb& 21:23 < bridge_> No, you can't disable the borrow checker 21:24 < bridge_> it’s bigger than the borrow checker 21:24 < bridge_> i edited my message 21:24 < bridge_> for clarity 21:24 < bridge_> unsafe lets you use unsafe functions and it lets you dereference raw pointers 21:26 < bridge_> Apparently I forgot a couple: 21:26 < bridge_> 21:26 < bridge_> - Dereference a raw pointer 21:26 < bridge_> - Call an unsafe function or method 21:26 < bridge_> - Access or modify a mutable static variable 21:26 < bridge_> - Implement an unsafe trait 21:26 < bridge_> - Access fields of a union 21:26 < bridge_> Apparently I forgot a couple: 21:26 < bridge_> - Dereference a raw pointer 21:26 < bridge_> - Call an unsafe function or method 21:26 < bridge_> - Access or modify a mutable static variable 21:27 < bridge_> - Implement an unsafe trait 21:27 < bridge_> - Access fields of a union 21:27 < bridge_> mutable statics 21:27 < bridge_> god 21:27 < bridge_> so it mostly doesn't make rust any "easier" 21:27 < bridge_> it’s just not necessary if you’re actually writing Rust 21:27 < bridge_> it’s needed for FFI and stuff 21:27 < bridge_> low level operations 21:27 < bridge_> embedded 21:28 < bridge_> but if you’re using pure rust you’d seldom encounter a need for unsafe 21:28 < bridge_> I'm aware it's used calling non-rust code, I just also expected it to disable all saftey features for some reason 21:28 < bridge_> the language still has to be semantically valid 21:28 < bridge_> "easier" not really, since the hardest part of rust is pleasing the borrow checker 21:28 < bridge_> yes 21:29 < bridge_> but it does allow you to convey information to the compiler that you otherwise cant, for example with your raw pointer powers you can craft two mutable references into one vec 21:29 < bridge_> I'm aware it's used calling non-rust code, I just also expected it to disable all safety features for some reason 21:29 < bridge_> this is technically completely safe since the references do not overlap, however safe rust doesn't allow it, since the borrow checker doesn't really understand how to do it 21:30 < bridge_> what does it mean to craft two mutable references into one vec 21:31 < bridge_> As in you can have 21:31 < bridge_> ```rust 21:31 < bridge_> let ref0 = &mut myvec[0]; 21:31 < bridge_> let ref1 = &mut myvec[1]; 21:31 < bridge_> ``` 21:32 < bridge_> This just won't compile, it won't let you borrow myvec twice mutably. However it is in reality safe, you need unsafe to craft these refs (or use the newly stabilized safe wrapper around that unsafe code) 21:32 < bridge_> wow 21:33 < bridge_> that makes the borrow checker look really stupid but it's probably me who doesn't understand why this is hard for it 21:37 < bridge_> actually wait, myvec is a resizable vector so how can you have a stable reference to element of it 21:38 < bridge_> the borrow checker’s job is to make sure that, if the data can be changed (e.g. mutable), it can only be referred to in one place at a time 21:38 < bridge_> rust fixes this? 21:38 < bridge_> otherwise the data changes under foot of all other code using that data 21:38 < bridge_> the borrow checker does more obviously 21:39 < bridge_> Well the only way to have a mutable reference to the items is to have a mutable reference to the vector itself. The borrow checker ensures that there is only ever one mutable reference to a vector. Thus without a mutable reference in some other location the vec can't change and thus the references are stable 21:39 < bridge_> Well the only way to have a mutable reference to the items is to have a mutable reference to the vector itself. The borrow checker ensures that there is only ever one mutable reference to an object. Thus without a mutable reference in some other location the vec can't change and thus the references are stable 21:40 < bridge_> That's also why the block I sent can't compile, it tries to borrow `myvec` twice, mutably, not allowed 21:40 < bridge_> ah so .pushback is disallowed so long as ref0 and ref1 are borrowed? 21:40 < bridge_> it’s a fundamental misalignment between the borrow checker’s principle & the actual safety of the operation at hand 21:40 < bridge_> Well assuming you craft those in a way that actually works, yes, those existing freeze the entire vector from mutating 21:41 < bridge_> A single mutable reference to an element of that vector would do the exact same thing btw, this is how the borrow checker ensures safety, it doesn't have anything to do with this special situation of split borrows 21:43 < bridge_> ok, well that explains why you can take a reference into a vec at all, but I still don't understand why the borrow checker doesn't let you take more than 1 at different indexes 21:43 < bridge_> it requires referring to the vec itself 21:43 < bridge_> mutable reference needs exclusivity to the actual data 21:43 < bridge_> but you're only getting access to the element, it can know that 21:44 < bridge_> theoretically 21:44 < bridge_> you can also make an internal mutability wrapper or something p easy 21:44 < bridge_> theres also new grammar for it that works in safe blocks, sounds like 21:44 < bridge_> so yeah 21:44 < bridge_> That's what it can't understand. It doesn't know that those two items are distinct objects 21:44 < bridge_> I see 21:44 < bridge_> borrow checker has skill issue 21:46 < bridge_> smth important ish 21:46 < bridge_> if u have a Vec and u hold a &Obj, it’s not like cpp where that’s guaranteed to just be chilling out there in memory. you need a mutable friendly mechanism on the vec itself to change the data at all 21:46 < bridge_> Now for why it can't be fixed that idk. I'm sure there is some deep mathematical reason 21:46 < bridge_> oh, that's what I was expecting initially xd 21:47 < bridge_> does this same thing happen with arrays? 21:47 < bridge_> surely not 21:48 < bridge_> best way to illustrate this i think; reborrowing it as mutable isn’t the same as a const cast where u can take away the const qualifier & then change the underlying data at your own discretion 21:48 < bridge_> the only time that kind of thing is possible, other things are afoot related to specific language features 21:48 < bridge_> Yep, it does 21:49 < bridge_> ok that's crazy 21:49 < bridge_> The borrow checker fundamentally is incapable of understanding that two references to the same block of memory are disjoint 21:50 < bridge_> For structs they have some extra magic that allows it 21:50 < bridge_> Idk why such magic is not possible for slices in general though 21:50 < bridge_> I was gonna say I don't often have a case where I want multiple references to an element in a vector, but that definitely happens with arrays 21:50 < bridge_> At least for constant indices 21:50 < bridge_> I was gonna say I don't often have a case where I want multiple references to elements in a vector, but that definitely happens with arrays 21:51 < bridge_> At least for continuous containers this shouldn't be so hard. but what do I know I'm no language engineer 21:51 < bridge_> in cpp i think situations like these are why iterators get invalidated 21:52 < bridge_> e.g. when iterating on an (ordered) map you can’t really add or remove items iirc 21:52 < bridge_> contiguous* 21:52 < bridge_> because it calls into question the integrity of the rest of the loop 21:54 < bridge_> I can imagine this being very annoying when implementing something like a cellular automata 21:55 < bridge_> I guess this is actually the exact same issue as the vector since you would obviously need to borrow the "whole array" to mem copy over a range of it so it can't just treat each element as a seperate variable 21:55 < bridge_> Exactly 21:56 < bridge_> Afaik there is only special sauce for structs where the borrow checker understands that fields of a struct are disjoint 21:57 < bridge_> yeah 21:57 < bridge_> i think it’s more that structs aren’t containers in memory but just language constructs 21:58 < bridge_> they’re usually represented a certain way but there’s no mandate that it all be the same blob right 21:58 < bridge_> They do suffer from the same fundamental issue though. Your mutable borrow of the struct is technically a mutable borrow of the entire object 21:58 < bridge_> same reason why certain fields can be mutable and some not 21:58 < bridge_> yeah 21:59 < bridge_> “x is behind a shared reference, did you mean to clone”? 22:14 < bridge_> uh oh 22:46 < bridge_> I found another scary sequence 22:46 < bridge_> ``` 22:46 < bridge_> 0 Antibot { data: b"..." } 22:47 < bridge_> 0 Joinver6 { cid: 0 } 22:47 < bridge_> 0 Join { cid: 0 } 22:47 < bridge_> 14 Message { cid: 0, msg: b"(aizessoa\x00\x00\xa4\x0cfluttertee\x00\x00\x80\xfe\x07\x80\xfe\x07" } 22:47 < bridge_> 15 PlayerReady { cid: 0 } 22:47 < bridge_> 15 Ddnetver { cid: 0, connection_id: ..., ddnet_version: ..., ddnet_version_str: "..." } 22:47 < bridge_> 16 PlayerNew { cid: 0, pos: (80, 432) } 22:47 < bridge_> 17 PlayerChange { cid: 0, pos: (80, 433), old_pos: (80, 432) } 22:47 < bridge_> 17 Antibot { data: b"..." } 22:47 < bridge_> 17 Antibot { data: b"..." } 22:47 < bridge_> 33 Antibot { data: b"..." } 22:47 < bridge_> 34 Antibot { data: b"..." } 22:47 < bridge_> 34 Antibot { data: b"..." } 22:47 < bridge_> 34 Antibot { data: b"..." } 22:47 < bridge_> 38 PlayerTeam { cid: 1, team: 1 } 22:47 < bridge_> 51 Message { cid: 0, msg: b"4\xa2\x9a\x02" } 22:47 < bridge_> 51 Message { cid: 0, msg: b"\x00\x7f&L\xddq\xa29b\xbb\xce\x0f\x94\xbb\xd8\x19\x13\x00" } 22:47 < bridge_> 51 Message { cid: 0, msg: b"\x00S\xbb(\xafBR:\xc9\x8f\xd3l\xcb\xc2\xa6\x03\xe3\x95\x16\xa4\x0c" } 22:47 < bridge_> 51 Input { cid: 0, input: [0, 112, -212, 0, 6, 1, 1, 4, 0, 0] } 22:47 < bridge_> 56 Message { cid: 1, msg: b"(debinsu1t\x00W\xc4\xa7O a\xc5\xa0k3d\x00\x88\x06whyner\x00\x01\x80\x80\xd8\r\x80\x80\xa8\x0c" } 22:47 < bridge_> 56 PlayerName { cid: 1, name: "debinsu1t" } 22:47 < bridge_> 58 PlayerReady { cid: 1 } 22:47 < bridge_> ``` 22:47 < bridge_> 22:47 < bridge_> There is no `Join { cid: 1 }` 22:57 < bridge_> well I suppose as long as you are using static indices (which is the only case where the borrow checker could know that they are separate), you *could* use a not-super-clean workaround ^^ 22:57 < bridge_> https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=538f35625e5a5c0f7095d17dede3ea3d 22:57 < bridge_> (just procrastinating learning for an exam tomorrow) 22:58 < bridge_> (there might be a prettier way to do this recently stabilized I think, though I don't remember what it is and Ryozuki posts far too much for me to dig it out) 22:59 < bridge_> https://doc.rust-lang.org/std/primitive.slice.html#method.get_disjoint_mut 22:59 < bridge_> Yes, this one 23:01 < bridge_> my solution is checked at compile time. (assuming they aren't optimized out in the std function) 23:01 < bridge_> I do not know what to do with these, I can't see any way this happens in the code. Are we somehow losing entire teehistorian chunks? Is there a way to join the server that we forgot to add `RecordPlayerJoin` to? Very confusing 23:02 < bridge_> is that a really old teehistorian? if yes, then can happen on map changes and is long fixed. 23:02 < bridge_> if not: someone else also found that issue recently and the cause is unknown afaik 23:03 < bridge_> If they didn't allow ranges that check wouldn't degenerate to O(n^2), I wonder if it's worth it 23:03 < bridge_> No, this is 2025-01-03, teehistorian version 2.9 23:04 < bridge_> yea ouch 23:04 < bridge_> maybe after map chanage after an ominous drop? 23:05 < bridge_> There is a bit of sus code around the map change thing, I'll try to dig out the teehistorian file that precedes this and see if this guy was there before it changed map 23:08 < bridge_> He joined RIGHT before a map change 23:08 < bridge_> That causes it 23:09 < bridge_> ah :/ 23:09 < bridge_> I also found an input issue with teehistorian that needs fixing, I'll create a merge request this weekend hopefully 23:09 < bridge_> ``` 23:09 < bridge_> 4172 Joinver6 { cid: 1 } 23:09 < bridge_> 4172 Join { cid: 1 } 23:09 < bridge_> 4223 Antibot { data: b"..." } 23:09 < bridge_> 4273 Input { cid: 0, input: [0, 514, -901, 0, 6, 1, 1, 4, 0, 0] } 23:09 < bridge_> 4274 ConsoleCommand { cid: -1, flag_mask: 4, cmd: "sv_reset_file", args: ["types/solo/flexreset.cfg"] } 23:09 < bridge_> 4274 ConsoleCommand { cid: -1, flag_mask: 4, cmd: "random_unfinished_map", args: ["1"] } 23:09 < bridge_> ``` 23:56 < bridge_> I tried SDL 2.0.16 (which was used for the old Emscripten version) and SDL 2.0.20 and they don't even compile with the current Emscripten version anymore. 23:56 < bridge_> 23:56 < bridge_> I noticed the following though: 23:56 < bridge_> - Building DDNet.html with Ninja produces a broken binary because CMake improperly escapes dollar signs for Ninja, or more like, there is no way to escape dollar signs for Ninja: https://gitlab.kitware.com/cmake/cmake/-/issues/16395 --> you have to use Makefiles or run the last linking step manually 23:56 < bridge_> - We could yield back control to the Emscripten event loop with `emscripten_sleep` after starting threads. That works around the pthread issue of the client hanging, but causes other cursed issues instead because it does weird unwinding of the stack or something. I experimented with these changes: 23:56 < bridge_> ```cpp 23:56 < bridge_> #if defined(CONF_PLATFORM_EMSCRIPTEN) 23:56 < bridge_> #include 23:56 < bridge_> #endif 23:56 < bridge_> // add this after thread_init in CHttp and CGraphicsBackend_Threaded so the threads can start 23:56 < bridge_> #if defined(CONF_PLATFORM_EMSCRIPTEN) 23:56 < bridge_> emscripten_sleep(5000); 23:56 < bridge_> #endif 23:56 < bridge_> // make buffer static so it does not get freed due to emscripten_sleep unwinding magic 23:56 < bridge_> static CCommandBuffer CmdBuffer(1024, 512); 23:56 < bridge_> // still results in weird "index out of bounds" exceptions elsewhere in graphics though though 23:56 < bridge_> ```