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

Distance option to prevent new towns to block old towns expansion. #7364

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

HydrolienF
Copy link

Description:

Add a new config option to prevent town encasement a bit better for older town.
The idea is that if a new town is created close to an older town, the old town will be able to claim more land between the 2 town.
New town could have choose an other location while the old town can't move. So it's unfair for the old town if it get claim blocked by a new town.


New Nodes/Commands/ConfigOptions:

New config option min_plot_distance_from_older_town_plot.
The option is disabled by default and can be enabled by making min_plot_distance_from_older_town_plot > min_plot_distance_from_town_plot


Relevant Towny Issue ticket:

It is the feature that we talk about in #7361


  • I have tested this pull request for defects on a server. The feature have worked in any case I have tested and I haven't found anything broken about claim distance.

By making this pull request, I represent that I have the right to waive copyright and related rights to my contribution, and agree that all copyright and related rights in my contributions are waived, and I acknowledge that the TownyAdvanced organization has the copyright to use and modify my contribution under the Towny License for perpetuity.

Copy link
Member

@LlmDl LlmDl left a comment

Choose a reason for hiding this comment

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

I was able to do a bit of a review, found some changes that should be made. I should be able to do a second review after the requested changes are made. Overall it looks decent.

HydrolienF and others added 5 commits April 16, 2024 18:57
…java

Co-authored-by: LlmDl <LlmDlio@gmail.com>
…java

Co-authored-by: LlmDl <LlmDlio@gmail.com>
…java

Co-authored-by: LlmDl <LlmDlio@gmail.com>
- Inverse the logic of the predicate to isIgnorableTown.
- Move public methods upper than the private one.
- Move predicate test
- Remove a non necessary anymore ==null test.
…thub.com:HydrolienF/Towny into min_plot_distance_from_town_plot_for_newest_town
@HydrolienF
Copy link
Author

Thanks for reviewing step by step and helping me doing a better pull request.
I have done the changes.
Let me know if it's look good or if there is somethings else to improve.

are blocked.

Extracts a few methods to keep things more readable.
@LlmDl
Copy link
Member

LlmDl commented Apr 30, 2024

I've gotten this about to the point that I'm happy with it.

There's a few things I don't really like. Namely, that we're essentially running the distance checks two times every time we test the distance. We are also currently testing the distance rules about twice every time there is claiming already. Once when the claiming selection is made, and then (somewhat redundantly,) checking it again in the proximity rules.

So we're basically going from testing distance twice to testing it four times with this PR.

@Warriorrrr what are you thoughts?

@HydrolienF
Copy link
Author

There's a few things I don't really like. Namely, that we're essentially running the distance checks two times every time we test the distance. We are also currently testing the distance rules about twice every time there is claiming already. Once when the claiming selection is made, and then (somewhat redundantly,) checking it again in the proximity rules.

So we're basically going from testing distance twice to testing it four times with this PR.

Something that we can do to avoid calculating distance multiple time is add a new function TownyWorld#notTooCloseToOtherTowns(WorldCoord worldCoord, Town town) and make distance test in it instead of in AreaSelectionUtil#worldCoordNotTooCloseToOtherTowns(Town town, WorldCoord worldCoord)

private static boolean worldCoordNotTooCloseToOtherTowns(Town town, WorldCoord worldCoord) {
		TownyWorld townyWorld = worldCoord.getTownyWorld();
		return townyWorld != null 
				&& townyWorld.getMinDistanceFromOtherTownsPlots(worldCoord, town) >= TownySettings.getMinDistanceFromTownPlotblocks()
				&& townyWorld.getMinDistanceFromOtherOlderTownsPlots(worldCoord, town) >= TownySettings.getMinDistanceFromOlderTownPlotblocks();
	}

whould become

private static boolean worldCoordNotTooCloseToOtherTowns(Town town, WorldCoord worldCoord) {
		TownyWorld townyWorld = worldCoord.getTownyWorld();
		return townyWorld != null 
				&& townyWorld.notTooCloseToOtherTowns(worldCoord, town);
	}

And we will add a new function in TownyWorld similar to getMinDistanceFromOtherTownsPlots(...) but where the 2 distance values and the 2 predicates will be tested after distance caclualtion.

public boolean notTooCloseToOtherTowns(Coord key, Town homeTown) {
	final Map<Integer, Predicate<Town>> minDistances = Map.of(TownySettings.getMinDistanceFromTownPlotblocks(), t -> false, TownySettings.getMinDistanceFromOlderTownPlotblocks(), t -> t.getRegistered() > homeTown.getRegistered());
	final int keyX = key.getX();
	final int keyZ = key.getZ();

	for(Town town : getTowns().values()){
		if (homeTown != null && townSkippedByProximityFilter(town, homeTown, t -> false)) // No skip with the predicate here because it will be done later by the map.
			continue;

		for (TownBlock b : town.getTownBlocks()) {
			if (!b.getWorld().equals(this)) continue;

			final int tbX = b.getX();
			final int tbZ = b.getZ();
			
			if (keyX == tbX && keyZ == tbZ)
				continue;
			
			final double distSqr = MathUtil.distanceSquared((double) tbX - keyX, (double) tbZ - keyZ);
			// If there is a town that is too close, return false.
			if (minDistances.entrySet().stream().anyMatch(entry -> distSqr >= entry.getKey() && !entry.getValue().test(town)))
				return false;
		}
	}
	return true;
}

I see this change having 3 advantages:

  • It do distance caclulation only once.
  • function stop as soon as it find a too close town instead of caclulating distance for each townplot.
  • it make it easy to add new distance test on some town. (Just add a new int and a new predicate on the map)

If getMinDistanceFromOtherTownsPlots(...) and getMinDistanceFromOtherOlderTownsPlots(...) are used only to see if a plot is too close from a town, then we can even remove completly this 2 functions and replace them by notTooCloseToOtherTowns(...).

@LlmDl let me know what you think about it. If you think it's a great way to do it, I can update the pull request.

@LlmDl
Copy link
Member

LlmDl commented Jun 2, 2024

I think the direction you're planning is the way to go yes, what would be really nice is if we had test coverage applied to the distance/proximity code. Then we'd know if something breaks.

@HydrolienF
Copy link
Author

Ok, then I will try to do a distance check function with a single for loop, link all current distance check to that function.
And add unit test about it.

@HydrolienF HydrolienF marked this pull request as draft June 3, 2024 08:44
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

2 participants