Fractal Softworks Forum

Please login or register.

Login with username, password and session length

Author Topic: [WIP][BlackMagic][541a] CharacterCreationPluginImpl multimod compatability bus  (Read 2960 times)

RawCode

  • Admiral
  • *****
  • Posts: 511
    • View Profile

This is blackmagic (reflections) based mod (that does not call obfuscated core methods for now) to allow multiple CharacterCreationPluginImpl scripts to load it's data into memory without major side effects.

Current implementation is:

Spoiler
Code: java
package data.scripts.plugins;

import com.fs.starfarer.api.campaign.CargoAPI.CrewXPLevel;
import com.fs.starfarer.api.characters.CharacterCreationPlugin;
import com.fs.starfarer.api.characters.MutableCharacterStatsAPI;

import data.scripts.world.RawCodeGen;
import data.usc.uscData;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.log4j.Logger;

public class CharacterCreationPluginImpl implements CharacterCreationPlugin {

// externals block
@SuppressWarnings("rawtypes")
public static List _getResponses = new ArrayList();
public static int _stage = 0;
public static
HashMap<String,ResponseImpl> _ResponsePool   = new HashMap<String,ResponseImpl>();
Logger fun = Logger.getLogger(this.getClass());

public static class ResponseImpl implements Response {
private String text;

public ResponseImpl(String text) {
this.text = text;
}

public String getText() {
return text;
}
}

public void eventbussurrogate1() throws Exception{
Class test = Class.forName("data.scripts.plugins.CharacterCreationPluginImpl_1");
Method inv = test.getMethod("main", null);
Method tt  = test.getMethod("getResponses", null);

inv.invoke(null, null);
tt.invoke(null, null);
}

public void eventbussurrogate() throws Exception{
Class<?> test = Class.forName("data.scripts.plugins.CharacterCreationPluginImpl_0");
Method inv = test.getDeclaredMethod("main", null);
Method tt  = test.getDeclaredMethod("getResponses", null);
fun.info("sur 1");
inv.invoke(null, null);
tt.invoke(null, null);
fun.info("sur 2");
}

private String[] prompts = new String[] {"1","2","3","4"};

public String getPrompt() {
if (_stage < prompts.length) {
return prompts[_stage];
}
return null;
}

public List getResponses() {
_getResponses = new ArrayList();

fun.info("TEST 1");

try {
fun.info("TEST 2");
eventbussurrogate();
fun.info("TEST 3");
} catch (Exception e) {
fun.info("TEST 4");
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
sw.toString();
fun.info(sw.toString()+ "FFFF");
fun.info("TEST 5");
}
try {
eventbussurrogate1();
} catch (Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
sw.toString();
fun.info(sw.toString()+ "FFFF");
}

fun.info("SIZE OF ARRAY OUTSIDE " + _getResponses.size());

return _getResponses;
}

public void submit(Response response, CharacterCreationData data) {
_stage++;
}

public void startingShipPicked(String variantId, CharacterCreationData data) {
}
}
[close]

Surrogates implementation is:

Spoiler
Code: java
package data.scripts.plugins;

import com.fs.starfarer.api.campaign.CargoAPI.CrewXPLevel;
import com.fs.starfarer.api.characters.CharacterCreationPlugin;
import com.fs.starfarer.api.characters.CharacterCreationPlugin.CharacterCreationData;
import com.fs.starfarer.api.characters.CharacterCreationPlugin.Response;
import com.fs.starfarer.api.characters.MutableCharacterStatsAPI;

import data.scripts.plugins.CharacterCreationPluginImpl.ResponseImpl;
import data.usc.uscData;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.log4j.Logger;

public class CharacterCreationPluginImpl_0 {
static Logger fun = Logger.getLogger("INVOKE");


@SuppressWarnings("unused")
public static void main(){
HashMap<String, ResponseImpl> _ResponsePool = CharacterCreationPluginImpl._ResponsePool;
fun.info("INSIDE 1");
_ResponsePool.put("LOL",new ResponseImpl(
"Served as a junior supply officer in an independent system's navy"));

_ResponsePool.put("LOL1",new ResponseImpl(
"Hired out as a gunner on a mercenary ship"));

_ResponsePool.put("LOL2",new ResponseImpl(
"Spent time as a co-pilot on a patrol ship in an independent system"));
fun.info("INSIDE 2");
}

public static void getResponses() {
List current = CharacterCreationPluginImpl._getResponses;
HashMap<String, ResponseImpl> _ResponsePool = CharacterCreationPluginImpl._ResponsePool;
fun.info("INSIDE 3");

fun.info("STAGE  " + CharacterCreationPluginImpl._stage);

switch (CharacterCreationPluginImpl._stage){
case 0:fun.info("case 1");
current.add(_ResponsePool.get("LOL")); break;
case 1:fun.info("case 2");
current.add(_ResponsePool.get("LOL1")); break;
case 2:fun.info("case 3");
current.add(_ResponsePool.get("LOL2")); break;
}
fun.info("INSIDE 4");

fun.info("SIZE OF ARRAY " + current.size());
}
}
[close]

Current version is proof of concept, a bit later (after few cups of coffe) i will try to implement class discovery method, that will search all suitable classes and load em.

Currently it able to load as many classes as needed as long as them follow CharacterCreationPluginImpl_NUMBER format.
I will cap number of attempts at 32 for now, soo every one will have nice chance to select free number and sucsessfuly load code.

NOTE it may fail on your machine due java.io limitation, PM me i will provide patch for this or remove any java.io calls (they used to log stacktraces only)


Edit: [code=java]  ~Trylo
« Last Edit: October 02, 2013, 01:45:54 PM by Trylobot »
Logged

RawCode

  • Admiral
  • *****
  • Posts: 511
    • View Profile

Fully working code + class loader.

How to use:

1) This is my special CharacterCreationPluginImpl that does not do anything by itself, merely loading other classes into game.
Entire mechanism based of reflections, it does not need links to other classes and will load anyway, ever if classes not present at moment of compilation.
It still WIP, still use java.io and still not cleaned up.
Unlikely to work with janino, soo you must jar your mod in order to use this feature.

Spoiler
Code
package data.scripts.plugins;

import com.fs.starfarer.api.campaign.CargoAPI.CrewXPLevel;
import com.fs.starfarer.api.characters.CharacterCreationPlugin;
import com.fs.starfarer.api.characters.MutableCharacterStatsAPI;

import data.scripts.world.RawCodeGen;
import data.usc.uscData;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;

public class CharacterCreationPluginImpl implements CharacterCreationPlugin {

@Deprecated
Logger fun = Logger.getLogger(this.getClass());
Logger log = fun;

// externals block
@SuppressWarnings("rawtypes")
public static List _getResponses  = null;
public static List _AvailableImps = new ArrayList();
public static int _stage    = 0;
public static                                      //
HashMap<String,ResponseImpl> _ResponsePool  = new HashMap<String,ResponseImpl>();
public static String[] _Prompts       = new String[8];
public static Response        _Response      = null;
public static CharacterCreationData _Data       = null;
public static String                _HullID    = "";
public static boolean _FIRSTRUN      = true;

public String getPrompt() {
if (_FIRSTRUN){
_FIRSTRUN = false;
EBUS_discover();
EBUS_execute("getPrompt");
}
if (_stage < _Prompts.length) {
return _Prompts[_stage];
}
return null;
}

public static class ResponseImpl implements Response {
private String text;

public ResponseImpl(String text) {
this.text = text;
}

public String getText() {
return text;
}
}
/*
public void eventbussurrogate1() throws Exception{
Class test = Class.forName("data.scripts.plugins.CharacterCreationPluginImpl_1");
Method inv = test.getMethod("main", null);
Method tt  = test.getMethod("getResponses", null);

inv.invoke(null, null);
tt.invoke(null, null);
}

public void eventbussurrogate() throws Exception{
Class<?> test = Class.forName("data.scripts.plugins.CharacterCreationPluginImpl_0");
Method inv = test.getDeclaredMethod("main", null);
Method tt  = test.getDeclaredMethod("getResponses", null);
fun.info("sur 1");
inv.invoke(null, null);
tt.invoke(null, null);
fun.info("sur 2");
}*/

public void EBUS_discover(){
String Base = "data.scripts.plugins.CharacterCreationPluginImpl_";

for(int i=0; i<31; i++){
try
{
Class<?> test = Class.forName("data.scripts.plugins.CharacterCreationPluginImpl_" + i);
test.getDeclaredMethod("init", null).invoke(null, null);
_AvailableImps.add(test);
log.info(test.getName() + " initialized.");
} catch (Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
sw.toString();
//fun.info(sw.toString()+ "FFFF");
}
        }
log.info(_AvailableImps.size() + " size of loaded array");
}

public void EBUS_execute(String S)
{
Iterator<Object> it = _AvailableImps.iterator();
while(it.hasNext())
{
        try {
        ((Class) it.next()).getDeclaredMethod(S, null).invoke(null, null);
} catch (Exception e){}
        }
}

public List getResponses() {
_getResponses = new ArrayList();

EBUS_execute("getResponses");
/*
fun.info("TEST 1");

try {
fun.info("TEST 2");
eventbussurrogate();
fun.info("TEST 3");
} catch (Exception e) {
fun.info("TEST 4");
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
sw.toString();
fun.info(sw.toString()+ "FFFF");
fun.info("TEST 5");
}
try {
eventbussurrogate1();
} catch (Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
sw.toString();
fun.info(sw.toString()+ "FFFF");
}

fun.info("SIZE OF ARRAY OUTSIDE " + _getResponses.size());
*/
return _getResponses;
}

public void submit(Response response, CharacterCreationData data) {
_Response = response;
_Data = data;

_stage++;

EBUS_execute("submit");
}

public void startingShipPicked(String variantId, CharacterCreationData data) {
_Data = data;
_HullID = variantId;
EBUS_execute("startingShipPicked");
}
}
[close]


2) this is sample loader, 99% vanilla (4 lines missing about basic hull).

Spoiler
Code
package data.scripts.plugins;

import com.fs.starfarer.api.campaign.CargoAPI.CrewXPLevel;
import com.fs.starfarer.api.characters.CharacterCreationPlugin;
import com.fs.starfarer.api.characters.CharacterCreationPlugin.CharacterCreationData;
import com.fs.starfarer.api.characters.CharacterCreationPlugin.Response;
import com.fs.starfarer.api.characters.MutableCharacterStatsAPI;
import com.fs.starfarer.api.fleet.FleetMemberAPI;

import data.scripts.plugins.CharacterCreationPluginImpl.ResponseImpl;
import data.usc.uscData;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.log4j.Logger;

public class CharacterCreationPluginImpl_0 {
static Logger fun = Logger.getLogger("INVOKE");


public static void Responce_put(String Tag,String Info){
CharacterCreationPluginImpl._ResponsePool.put(Tag, new ResponseImpl(Info));
}

public static void Responce_rem(String Tag){
CharacterCreationPluginImpl._ResponsePool.remove(Tag);
}

@SuppressWarnings("unchecked")
public static void addResponse(String Tag)
{
CharacterCreationPluginImpl._getResponses.
add(CharacterCreationPluginImpl._ResponsePool.get(Tag));
}

public static Response Responce_get(String Tag){
return CharacterCreationPluginImpl._ResponsePool.get(Tag);
}

@SuppressWarnings("unused")
public static void init()
{
fun.info("INITIALIZATION");
Responce_put("SUPPLY_OFFICER" , "Served as a junior supply officer in an independent system's navy");
Responce_put("GUNNER" , "Hired out as a gunner on a mercenary ship");
Responce_put("ENGINEER" , "Found employment as an assistant engineer on an exploration vessel");
Responce_put("COPILOT" , "Spent time as a co-pilot on a patrol ship in an independent system");
Responce_put("SOMETHING_ELSE_1" , "Did something else");
Responce_put("ADJUTANT" , "Served as an adjutant in the Hegemony Navy");
Responce_put("QUARTERMASTER" , "Performed the duties of a quartermaster on an independent warship");
Responce_put("HELM" , "Helmed a patrol ship operating in a backwater system");
Responce_put("COMBAT_ENGINEER" , "Took over the duties of chief combat engineer during a lengthy campaign");
Responce_put("SOMETHING_ELSE_2" , "Did something else");
}

static int Capacity = 0;
static String[] tmps;

public static void setPrompt(String Tag, int Position)
{
if (Position > tmps.length)
{
String[] old = tmps.clone();
tmps = new String[Position];
for (int i = 0; i < old.length; i++)
{
tmps[i] = old[i];
}
}
tmps[Position] = Tag;
}

public static void getPrompt() {
tmps = CharacterCreationPluginImpl._Prompts;
setPrompt("Early in your career, you...",0);
setPrompt("More recently, you...",1);
CharacterCreationPluginImpl._Prompts = tmps;

}

public static void getResponses(){
fun.info("getResponses Called");
switch (CharacterCreationPluginImpl._stage)
{
case 0:
{
fun.info("case 0 Called");
addResponse("SUPPLY_OFFICER");
addResponse("GUNNER");
addResponse("ENGINEER");
addResponse("COPILOT");
addResponse("SOMETHING_ELSE_1");
break;
}
case 1:
{
fun.info("case 1 Called");
addResponse("ADJUTANT");
addResponse("QUARTERMASTER");
addResponse("HELM");
addResponse("COMBAT_ENGINEER");
addResponse("SOMETHING_ELSE_2");
break;}
default: {return;}}
}

public static void submit() {

int _stage = CharacterCreationPluginImpl._stage;
Response _Response = CharacterCreationPluginImpl._Response;
CharacterCreationData _Data = CharacterCreationPluginImpl._Data;

if (_stage == 0) { // just in case
_Data.addStartingShipChoice("shuttle_Attack");
}

MutableCharacterStatsAPI stats = _Data.getPerson().getStats();
if (_Response == Responce_get("SUPPLY_OFFICER")) {
stats.increaseAptitude("leadership");
stats.increaseSkill("fleet_logistics");
stats.increaseSkill("command_experience");
_Data.getStartingCargo().getCredits().add(3000f);
} else if (_Response == Responce_get("GUNNER")) {
stats.increaseAptitude("combat");
stats.increaseSkill("ordnance_expert");
stats.increaseSkill("target_analysis");
_Data.getStartingCargo().getCredits().add(3000f);
} else if (_Response == Responce_get("ENGINEER")) {
stats.increaseAptitude("technology");
stats.increaseSkill("field_repairs");
stats.increaseSkill("mechanical_engineering");
_Data.getStartingCargo().getCredits().add(3000f);
} else if (_Response == Responce_get("COPILOT")) {
stats.increaseAptitude("combat");
stats.increaseSkill("helmsmanship");
stats.increaseSkill("evasive_action");
_Data.getStartingCargo().getCredits().add(3000f);
} else if (_Response == Responce_get("SOMETHING_ELSE_1")) {
stats.addAptitudePoints(1);
stats.addSkillPoints(2);
_Data.getStartingCargo().getCredits().add(1000f);
}
else if (_Response == Responce_get("ADJUTANT")) {
stats.increaseAptitude("leadership");
stats.increaseSkill("fleet_logistics");
stats.increaseSkill("advanced_tactics");
_Data.addStartingShipChoice("lasher_Standard");
_Data.getStartingCargo().getCredits().add(3000f);
} else if (_Response == Responce_get("QUARTERMASTER")) {
stats.increaseAptitude("technology");
stats.increaseSkill("navigation");
stats.addSkillPoints(1);
_Data.addStartingShipChoice("wolf_CS");
_Data.getStartingCargo().getCredits().add(3000f);
} else if (_Response == Responce_get("HELM")) {
stats.increaseAptitude("combat");
stats.increaseSkill("helmsmanship");
stats.increaseSkill("evasive_action");
_Data.addStartingShipChoice("vigilance_Standard");
_Data.getStartingCargo().getCredits().add(3000f);
} else if (_Response == Responce_get("COMBAT_ENGINEER")) {
stats.increaseAptitude("combat");
stats.increaseSkill("damage_control");
stats.increaseSkill("flux_modulation");
_Data.addStartingShipChoice("lasher_Standard");
_Data.getStartingCargo().getCredits().add(3000f);
} else if (_Response == Responce_get("SOMETHING_ELSE_2")) {
stats.addAptitudePoints(1);
stats.addSkillPoints(2);
_Data.addStartingShipChoice("hound_Assault");
_Data.getStartingCargo().getCredits().add(1000f);
}
}

public static void startingShipPicked() {
CharacterCreationData data = CharacterCreationPluginImpl._Data;
String variantId = CharacterCreationPluginImpl._HullID;

MutableCharacterStatsAPI stats = data.getPerson().getStats();
stats.addAptitudePoints(1);
stats.addSkillPoints(2);

if (variantId.equals("vigilance_Standard")) {
data.getStartingCargo().addFuel(20);
data.getStartingCargo().addSupplies(30);
data.getStartingCargo().addCrew(CrewXPLevel.REGULAR, 20);
data.getStartingCargo().addMarines(5);
} else
if (variantId.equals("lasher_Standard")) {
data.getStartingCargo().addFuel(20);
data.getStartingCargo().addSupplies(20);
data.getStartingCargo().addCrew(CrewXPLevel.REGULAR, 40);
data.getStartingCargo().addMarines(10);
} else
if (variantId.equals("wolf_CS")) {
data.getStartingCargo().addFuel(20);
data.getStartingCargo().addSupplies(20);
data.getStartingCargo().addCrew(CrewXPLevel.REGULAR, 22);
data.getStartingCargo().addMarines(7);
} else
if (variantId.equals("shuttle_Attack")) {
data.getStartingCargo().addFuel(10);
data.getStartingCargo().addSupplies(10);
data.getStartingCargo().addCrew(CrewXPLevel.REGULAR, 10);
data.getStartingCargo().addMarines(3);
} else
if (variantId.equals("hound_Assault")) {
data.getStartingCargo().addFuel(30);
data.getStartingCargo().addSupplies(40);
data.getStartingCargo().addCrew(CrewXPLevel.REGULAR, 25);
data.getStartingCargo().addMarines(15);
} else {
data.getStartingCargo().addFuel(20);
data.getStartingCargo().addSupplies(20);
data.getStartingCargo().addCrew(CrewXPLevel.REGULAR, 20);
data.getStartingCargo().addMarines(10);
}
}

}
[close]

Loader will attempt to load first 32 classes with name CharacterCreationPluginImpl_X where X is integer from 0 to 31.

This is sample of class that edit second caption of character setup to "lol" and nothing else.

Spoiler
Code
package data.scripts.plugins;

import com.fs.starfarer.api.campaign.CargoAPI.CrewXPLevel;
import com.fs.starfarer.api.characters.CharacterCreationPlugin;
import com.fs.starfarer.api.characters.CharacterCreationPlugin.CharacterCreationData;
import com.fs.starfarer.api.characters.CharacterCreationPlugin.Response;
import com.fs.starfarer.api.characters.MutableCharacterStatsAPI;
import com.fs.starfarer.api.fleet.FleetMemberAPI;

import data.scripts.plugins.CharacterCreationPluginImpl.ResponseImpl;
import data.usc.uscData;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.log4j.Logger;

public class CharacterCreationPluginImpl_26 {
static Logger fun = Logger.getLogger("INVOKE");


public static void Responce_put(String Tag,String Info){
CharacterCreationPluginImpl._ResponsePool.put(Tag, new ResponseImpl(Info));
}

public static void Responce_rem(String Tag){
CharacterCreationPluginImpl._ResponsePool.remove(Tag);
}

@SuppressWarnings("unchecked")
public static void addResponse(String Tag)
{
CharacterCreationPluginImpl._getResponses.
add(CharacterCreationPluginImpl._ResponsePool.get(Tag));
}

public static Response Responce_get(String Tag){
return CharacterCreationPluginImpl._ResponsePool.get(Tag);
}

@SuppressWarnings("unused")
public static void init()
{
}

static int Capacity = 0;
static String[] tmps;

public static void setPrompt(String Tag, int Position)
{
if (Position > tmps.length)
{
String[] old = tmps.clone();
tmps = new String[Position];
for (int i = 0; i < old.length; i++)
{
tmps[i] = old[i];
}
}
tmps[Position] = Tag;
}

public static void getPrompt() {
tmps = CharacterCreationPluginImpl._Prompts;
setPrompt("Lol",1);
CharacterCreationPluginImpl._Prompts = tmps;
}

public static void getResponses(){
}

public static void submit() {
}

public static void startingShipPicked() {
}

}
[close]

Similar modifications can be done to any class, as long as mods follow rules they will be compatable and wont crash eachother randomly.
Logged

TJJ

  • Admiral
  • *****
  • Posts: 1905
    • View Profile

Looks interesting, but your naming conventions made my eyes bleed  :-\
Logged

RawCode

  • Admiral
  • *****
  • Posts: 511
    • View Profile

its WIP!

also nobody want to implement this system, there is no reason to maintain it.
Logged