Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PhaseTracker 1.14 Upgrade #2476

Merged
merged 5 commits into from Feb 2, 2020
Merged

PhaseTracker 1.14 Upgrade #2476

merged 5 commits into from Feb 2, 2020

Conversation

gabizou
Copy link
Member

@gabizou gabizou commented Jan 12, 2020

This leaves a lot to be desired currently as it's a WIP.

What's the update?

The PhaseTracker is essentially an adaptation of pairing server-focused changes occurring in a World to an Event factory with specific Cause population based on the processing of a IPhaseState. If that's enough to make you ask "huh??" then it might be recommended to read a little bit about Events and their causes. The PhaseTracker is a wedged implementation of logging BlockState, BlockEntity, Entity, and ItemStack changes within a World during specific operations, such as:

  • An Entity performing work during a Game tick
  • A Player interacting with a block
  • A Block "growing" into a different Block
    etc.

Because of how intrinsically "wired" the system is to the engine, it needs hooks in various areas of the Game logic to say "hey, we're gonna be ticking an Entity here!" Currently, for stable-7 or Minecraft 1.12, it's doing this mostly well (sans some incompatibility with some mods and their design, no fault of theirs, just differences in how the system needs to be improved), but as it's shown, it's still very much tied to the game's core logic.

Ok, so update the hooks then?

Of course! How could I be so blind! Just update the tens of thousands of lines that make the system... Ok, it's not really tens of thousands, it's closer to maybe 20k, but short of the hooks, the actual logic processes need to be reviewed and re-analyzed with caution since 1.14's block logic has changed enough that some parts of the system are needing to be re-engineered.

Core concepts of the Update

PhaseTracker per Thread

With the PhaseTracker being so integral to many systems in the game logic, and to support client sided effects and events, along with asynchronous world generation events, the PhaseTracker is redesigned to have two primary instances (CLIENT and SERVER) while the purposed getInstance() will return a Thread appropriate variant. Because of this, we'll be able to appropriately associate individual PhaseTrackers to pooled PhaseContexts via PooledPhaseState. What this means is that the pools of recycled contexts will be per-thread, and furthermore be sanity checked to the PhaseTracker instance that created them. Uniquely, this will sanity check closures of PhaseContexts to avoid asynchronous closures without knowing which thread originally entered said state.

Splitting the PhaseTracker associated code from all Mixins into a separate package

This is mostly an organizational effort to verify "who does what and why". When looking at WorldServerMixin, it's difficult to read as to what this method is doing and why, except that it's interacting with a "proxy" for some reason. This, on top of several scattered areas where we have PhaseState entries, it becomes difficult to see what sections of code are to support the PhaseTracker, using the PhaseTracker for context population, or simply using the PhaseTracker for throwing events. Lastly, because testing of Sponge code without the PhaseTracker involvement and possible bugs/side effects, there's no "switch" to flip, except by removing the main hooks and hoping nothing else breaks in the process of those missing hooks.

Likewise, because the injections/redirects/etc. would now be in a separate Mixin bundle, we can too prefix those with tracker$ to further identify what methods are coming from where.

So here's a laundry list of things I need to have done for the update:

  • Update BlockTransaction to keep track of neighbor "shape updates" being requested, etc.
  • Update SpongeBlockProxy to be a simplified IWorld proxy so fewer mixins have to exist
  • This is up for debate, and only possible in 1.13+ because of the introduction of the super interface. Since we can implement IWorld, the contracts for various block methods only actually require an IWorld instance, allowing us to better track changes and TileEntity additions.
  • Review whether there's enough "hooks" in some mixins to split out into another mixing package/plugin
  • This would theoretically allow us to entirely disable the PhaseTracker without disabling events being thrown
  • We'd be able to improve visibility of what mixins/injections are for what purpose, when some are stubs or proxy-related injections.
  • Review BlockChangeFlag API exposure, we have a bit we can expand on for better plugin control

Signed-off-by: Gabriel Harris-Rouquette <gabizou@me.com>
…umenting the block physics process to re-re-re-re-re-rewrite the core of logging these changes.

Signed-off-by: Gabriel Harris-Rouquette <gabizou@me.com>
…tarted WorldMixin and ServerWorldMixin.

Signed-off-by: Gabriel Harris-Rouquette <gabizou@me.com>
@SuppressWarnings("UnstableApiUsage")
public class EventContextKeySupplier {

public static Stream<EventContextKey<?>> stream() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Zidane if you can, please tell me this is the correct way of doing it for Sponge provided types?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would go into the Stream package and be named like the other stream classes are.

public SpongeSpawnType(CatalogKey key) {
private boolean isForced = false;

public SpongeSpawnType(CatalogKey key, String name) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete name parameter unless it is a NamedCatalogType.

}

public SpongeEventContextKey(String id, String name, Class<T> allowed) {
public SpongeEventContextKey(CatalogKey id, TypeToken<T> allowed) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

id -> key

import java.util.Map;
import java.util.StringJoiner;

public final class BlockChangeFlagManager {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are not catalogs, correct?

final Map<String, Boolean> autoFixedTiles = trackerConfig.getAutoFixedTiles();
final boolean contained = autoFixedTiles.containsKey(type.getId());
final boolean contained = autoFixedTiles.containsKey(type.toString());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type.getKey().getFormatted()

}
} catch (final Throwable throwable) {
final CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Exception while updating neighbours");
final CrashReportCategory crashreportcategory = crashreport.makeCategory("Block being updated");
crashreportcategory.addDetail("Source block type", () -> {
try {
return String.format("ID #%d (%s // %s)", Block.getIdFromBlock(sourceBlock),
return String.format("ID #%d (%s // %s)", Registry.BLOCK.getId(sourceBlock),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not pass back IDs to crash reports, pass their keys

*
* @param pos The position of the block state to set
* @param newState The new state
* @param flag The notification flags
* @return True if the block was successfully set (or captured)
*/
@SuppressWarnings("rawtypes")
public boolean setBlockState(final ServerWorldBridge mixinWorld, final BlockPos pos, final net.minecraft.block.BlockState newState, final BlockChangeFlag flag) {
public boolean setBlockState(final ServerWorldBridge mixinWorld, final BlockPos pos,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mixinWorld -> worldBridge

}

// Sponge Start - At this point, we can stop and check for captures.
// Sponge Start - At this point, we can stop and check for captures;
Copy link
Member

@Zidane Zidane Jan 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.

@@ -1169,115 +857,21 @@ public boolean spawnEntityWithCause(final World world, final Entity entity) {
* @return True if the entity spawn is on the main thread.
*/
public static boolean isEntitySpawnInvalid(final Entity entity) {
if (Sponge.isServerAvailable() && (Sponge.getServer().isMainThread() || SpongeImpl.getServer().isServerStopped())) {
if (Sponge.isServerAvailable() && (Sponge.getServer().onMainThread() || SpongeImpl.getServer().isServerStopped())) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use SpongeImpl method to collapse first two branches

@@ -84,25 +85,19 @@
import org.spongepowered.common.event.tracking.phase.tick.NeighborNotificationContext;
import org.spongepowered.common.event.tracking.phase.tick.TickPhase;
import org.spongepowered.common.mixin.accessor.world.server.ServerWorldAccessor;
import org.spongepowered.common.registry.type.event.SpawnTypeRegistryModule;
import org.spongepowered.common.registry.builtin.supplier.SpawnTypeSupplier;
Copy link
Member

@Zidane Zidane Jan 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not reference supplier classes in common code. Simply do SpawnTypes.BLAH.get()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't if the spawn type is an internal one. It's not exposed to the API for the reason that it's internal use only but still detectable for plugins to interpret for their own uses.

public static void tickEntity(final Consumer<net.minecraft.entity.Entity> consumer, final net.minecraft.entity.Entity entity) {
checkArgument(entity instanceof Entity, "Entity %s is not an instance of SpongeAPI's Entity!", entity);
checkNotNull(entity, "Cannot capture on a null ticking entity!");
final EntityBridge mixinEntity = (EntityBridge) entity;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mixinEntity -> entityBridge

import java.util.function.Supplier;
import java.util.stream.Stream;

public class SpawnTypeSupplier {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, stream package and named like the stream classes.

In all honesty, the supplier classes are simply for supplying Vanilla registry entries.


public class SpawnTypeSupplier {

public static SpawnType FORCED = new SpongeSpawnType(CatalogKey.sponge("forced"), "Forced").forced();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason this is here? Why isn't this in the API?

Signed-off-by: Gabriel Harris-Rouquette <gabizou@me.com>
…n thread on server controlled worlds.

Signed-off-by: Gabriel Harris-Rouquette <gabizou@me.com>
@gabizou gabizou merged commit 4a68ea2 into api-8 Feb 2, 2020
@gabizou gabizou deleted the api8/tracker branch March 11, 2020 06:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants