@Histidine: Yeah, your interpretation of the permissions is correct, and the source should be available. If you (or anyone else for that matter) are really interested in fixing the bug, here's some information I gave to DR about it back when he was looking into doing something similar:
Spoiler
The main thing to avoid is the crash, so I'll describe why that happened in detail. The primary cause was that the CampaignEventManager was trying to create a recent unrest event after the colony fleet had been destroyed (after which I transferred its market to a different entity). In order to throw the NPE, this sequence of events would have to occur, in this order:
-Colony fleet experiences a food shortage
-Colony fleet is destroyed and the market is moved to a different entity in an inaccessible star system
-Food shortage ends and procs a recent unrest event
java.lang.NullPointerException
at com.fs.starfarer.api.impl.campaign.events.RecentUnrestEvent.startEvent(RecentUnrestEvent.java:20)
at com.fs.starfarer.campaign.events.CampaignEventManager.startEvent(Unknown Source)
at com.fs.starfarer.api.impl.campaign.events.BaseEventPlugin.increaseRecentUnrest(BaseEventPlugin.java:265)
at com.fs.starfarer.api.impl.campaign.events.FoodShortageEvent.endEvent(FoodShortageEvent.java:615)
at com.fs.starfarer.api.impl.campaign.events.FoodShortageEvent.advance(FoodShortageEvent.java:374)
at com.fs.starfarer.campaign.events.CampaignEventManager.advance(Unknown Source)
at com.fs.starfarer.campaign.CampaignEngine.advance(Unknown Source)
When recent unrest starts it runs BaseEventPlugin.init() (or so I assume)
public void init(String eventType, CampaignEventTarget eventTarget) {
this.eventType = eventType;
this.eventTarget = eventTarget;
if (eventTarget.getEntity() != null) {
market = eventTarget.getEntity().getMarket();
faction = eventTarget.getFaction();
entity = eventTarget.getEntity();
}
if (market == null) {
statModId = eventType + "_" + UUID.randomUUID().toString();
} else {
statModId = eventType + "_" + market.getId() + "_" + UUID.randomUUID().toString();
}
Global.getSector().addListener(this);
}
Because the market has already been removed from the entity, the 'market' variable is assigned null on line 06. So, when RecentUnrestEvent.startEvent() runs, it throws the NPE when trying to access 'market.'
public void startEvent() {
super.startEvent();
conditionToken = market.addCondition(Conditions.RECENT_UNREST, true, this);
}
As you can see, there are plenty of potential ways to fix this specific issue, so you shouldn't have trouble avoiding it. I can only guess at the causes of most of the less serious issues, but they all seemed to stem from the market being temporary and/or nomadic. The problem with unconventional markets (and the reason I've been avoiding fixing ICE) is that it's easy to cause unintended side effects that are often difficult to find.
My memory on the subject was much fresher than it is now, so I can't really provide any clues better than that. Some things about the campaign layer have been updated since I messed with this stuff though, so things may have changed. In fact, Alex fixed up the RecentUnrestEvent to handle cases like this, so the symptoms of this particular bug might be different or even non-existant. The real problem with dealing with this bug and ones like it is how long it can take to duplicate them.