Names can be faked, and even real names are not a great indicator.
Unless you have some very specific cultural knowledge you could not make even vaguely useful deductions about my location, nationality, culture, ethnicity etc. from my name. I get a lot of wrong guesses though!
It does, it's called DocumentsUI and provides the system file picker. However its main entry point is often hidden, and imo it's still somewhat suboptimal/limited in certain aspects. But it's the only way to access the private Android/data directory right now.
Material Files also provides the ability to easily open DocumentsUI for browsing Android/data.
Material Files also allows you add a shortcut to the stock file manager (DocumentsUI), using "Add storage" > "Android/data" from app drawer. You may even manually edit the shortcut to point to a specific subdirectory inside Android/data, and I plan to make that easier (so that you don't need to manually URI-encode your path if you edit) in an upcoming version.
Different design (Material 2 or 3 v.s. Material 1 mixed with Holo), different UX (e.g. multi-window v.s. dual-pane), Open source v.s. closed source, probably somewhat richer feature set from Total Commander (not entirely sure, didn't try it in depth).
Naming is hard, and I already have to implement file management :P
Just kidding. The reason is that I started the project mostly because I wanted a file manager with proper Material Design, and it's very unlikely that I (or AOSP) adopt a completely different design system.
Yes, and this app adopted it as well. There's a toggle in app settings for Material 3 v.s. the old Material 2 right now, and there used to be one between Material 1 and 2.
It's the same for all file managers and the previous way of granting access is now patched via security fixes. If it's still working for you that's very likely because you granted the access before it was fixed, and if you move to a new phone you also likely won't be able to do that any more.
Thanks for that.
I feel this video isn't particularly good after watching it. Surely that could be any ferrous material thats interacting with the field/inductive eddies there.
It isn't physically possible for a ferrous material to levitate above a magnet like that, when the magnetic poles are reversed. Basically you could structure it in such a way to generate a ferrous material levitating above a magnetic north pole, but flip the magnet around and you won't see the levitation. They flip the magnet around in this video.
Under the guidance of Professor Haixin Chang, postdoctor Hao Wu and PhD student Li Yang from the School of Materials Science and Technology of Huazhong University of Science and Technology successfully for the first time verified the LK-99 crystal that can be magnetically levitated with larger levitated angle than Sukbae Lee‘s sample at room temperature. It is expected to realize the true potential of room temperature, non-contact superconducting magnetic levitation.
Thanks for the explanation! I happened to be thinking of similar multithreading ideas (chunks and ping-ponging states) recently, but was stuck on what happens when computation happens across chunks in multiple threads.
e.g. an object move across chunk border, or an object interaction affecting two objects in different chunks.
Could you elavorate on your approach about these? I'm really curious
Good follow-up question! As for the read state, you're already golden in the sense that you only read data and don't update it. If a chunk needs world or entity data from a neighboring chunk—or even halfway across the world—you can just follow pointers there and you know the data is available and accurate.
For the write state, you're absolutely right that entities will need to transition between chunks. The server looks up the destination in the "active chunk set" I mentioned and then uses per-chunk std::vector<>s guarded by a mutex to push "migrating" entities. This requires a mutex since multiple CPUs/cores/threads/chunks can be pushing entities to the same destination chunk. These mutexes largely have zero to little contention.
Does that answer your question in an acceptable way?
Thanks! The idea of having a list of migrating entities per chunk is good, and it avoids locking on the list of all entities in a chunk. But I still have some confusion about it:
> The server looks up the destination in the "active chunk set" I mentioned
but what if the entity teleports to an inactive chunk?
> and then uses per-chunk std::vector<>s guarded by a mutex to push "migrating" entities.
Will these migrating entities participate in later calculation within the same tick, or are they excluded? If excluded, what happens when there are two mechanisms that mutates that entity, but the first one put it into the migrating list of another chunk? If included, the thread of which chunk will continue this calculation?
And when some kind of mechanism mutates two entities in different chunks, which thread is chosen to run this mutation? And how does it mutate the state for an entity in the other chunk?
Meanwhile since you mentioned ticks, (just to be sure) are you using a barrier per tick to wait for all threads to finish the same tick? When are the migrating entities merged into the main list, and are you using another barrier for that?
Really getting into the nitty-gritty here. If an entity migrates to an inactive chunk, I keep the entity "on hold" and add a "chunk set mutation" to the active chunk set so that the set is expanded in the next tick. The processing thread for this newly added chunk sees that no data exists yet and synchronizes the state from the backend during the subsequent tick. On this tick the entity gets added to the new chunk write state and disappears quietly from the old chunk in the next tick since it isn't "on hold" or copied anymore.
As for the processing per tick: the entire active chunk set gets semi-randomly processed by threads. You'd be absolutely right that the order of operations isn't always deterministic. An entity not being processed for a tick doesn't happen since the old chunk will still process entities that are "on hold", but only perform a subset of operations. Double-processing does occur and this is sometimes visible while playing with a chat message being duplicated. This happens pretty rarely and I took care to make sure occasional double-processing doesn't interfere with gameplay mechanics.
As for synchronization: yes, each tick all processing threads join together and the server performs minor central processing and scheduling. Then the next tick begins and all threads are started again.
I see. It seems sometimes we do have to live with non-deterministic behaviors or even small errors.
I'm still unsure about the following question from your answers:
> when some kind of mechanism mutates two entities in different chunks, which thread is chosen to run this mutation? And how does it mutate the state for the entity in the other chunk?
My initial thought was this would require adding locking on every entity, since it might be accessed from another chunk/thread. But that's clearly not ideal. Maybe have a separate list for outside-chunk changes, and merge them with internal changes later?
And regarding having two states: How/when are you sending world state to clients? My thought was to clone the current state periodically and send it to clients (can sending takes less than one tick? I don't have an idea). Sending state to clients seems to be needed if the game needs client side predication, hence this question.
I think your first question is actually supposed to be: what if a single (not two) entity is modified from multiple chunks?
All external effects that can happen to an entity are written to an atomic integer that tracks healing points, damage points and effects like poison and stunning. Angeldust currently runs on CPUs supporting atomic integers everywhere, so this doesn't have to fall back to mutexes. On platforms/compilers without atomic integers I have a software-fallback that uses mutexes for this, but currently it's not used anywhere.
Most world state is compiled into flat byte arrays each tick to save processing time, since many clients can be in the same area at once. The server bundles these world state byte arrays for chunks near a client together and sends them over the network. The client interpolates from these updates. What you see on screen lags behind subtly to make sure animations are smooth. I think this happens in most online games since you don't really want to extrapolate.
Angeldust's game pace and mechanics are designed so that the interpolation doesn't affect your aiming accuracy or game experience too much.