Geoff asked me for some additional feedback regarding a potential replacement of FOCS with python.
First of all I would like to clarify what I consider 'FOCS'. 'FOCS' is the self-designed set of domain specific languages that is used to describe the game entities like ship designs, species, technologies, … and so on. The technical parts of it are
- the text files, which contain the description of game entities in FOCS syntax. Those are located in the most recent development version in the `default/scripting` directory.
- the internal `freeorionparser` library, which, as the name implies, contains the C++ implementation for the FOCS parser using the boost::spirit v2(? not quite sure about the version) library.
- To some degree the C++ implementation of ValueReferences, Conditions and Effects as some FOCS syntax specifics 'bleed' into the design of those (e.g. the dot access syntax for dependent properties like 'Source.Owner', 'Target.SizeAsDouble', 'LocalCandidate.MaxStructure').
- Replace the text files with some other, to be determined, scripting language (python would be the obvious choice here, because it already exists).
- Remove the 'freeorionparser' library.
- Implement a some binding code to connect the ValueRef, Conditions and Effects with the new scripting language (with python that would be the boost::python binding library).
- Remove existing FOCS bleeding in the ValueRef, Conditions and Effects code design.
The freeorionparser takes a lot of time to compile in the best case. When taking the current offers of build services like travisci or appveyor, which provide only a limited amount of build time for open source project, the general idea is not realizable. In the worst case we've already seen that the current code pushes the compiler implementation beyond it's limitations. On windows this manifests as compiler crash/stack overflow. So far we worked around this by splitting up the various compiler statements into multiple files which drives up the compile further. To throw in some numbers about the compile time (single build process without any cache mechanism used on my machine, using time make <targetname> in a build order that doesn't add up dependent targets, leaving out GiGi because it doesn't matter that much):
- freeorioncommon: 10m 11.815s
- freeorionparse: 24m 43.129s
- freeoriond: 4m 14.960s
- freeorionca: 3m 14.470s
- freeorion: 18m 8.131s
The final problem with using FOCS is the absence of any support tools. There is no IDE, no automatic documentation extraction, the debugging capabilities are crude. A lot of things need to worked around like getting the correct line number in case of a syntax error (done by putting the includes at the bottom of the file). That are things I expect from every language.
So what we currently have with FOCS:
- An unmaintained Notepad++ syntax highlighting tool, which is probably way behind the current capabilities of FOCS.
- A manually maintained documentation in the Wiki I don't know how outdated it is (dbenage-cx did a decent job according to the wiki page history).
- No testing except the programmer smartness and luck. The automatic tests weren't touched for ages and they test only things like 'is this valid syntax' and not 'when writing this statement does it create the proper domain object' so I consider them nearly useless (we want to test our code, not the boost::spirit code).
About the 'FOCS is for non coders' argument and its variants:
I just can't agree with it. Consider the following use cases:
User A has never programmed a single line of code and is not able to abstract concepts like 'Target', 'EnqeueLocation', … even when looking at the manual. He will fail regardless of the language used.
User B has never programmed a single line of code but can abstract the game concepts with the help of the manual. He will struggle on minor things like using wrong attribute order, leaving out a quote and so on but he will get his change somehow into the game. The language doesn't matter as much as you would think because the user starts with zero knowledge and stumbling blocks are there in any language.
User C is proficient in general programming but does not know python. He has to learn either FOCS or python, depending on the scenario, to contribute to the project or change the game to his wishes.
User D is proficient in python will have no problem changing existing python code, but will need to learn FOCS, depending on the scenario.
About the syntax: the syntax is as complex or easy as we chose them to be (@MatGB don't consider existing python in the game as good reference to evaluate the complexity of the language in general. It tries to solve several complex problems and it's good but not perfect code. Also keep in mind that python gained a lot of it's popularity by being the 'first to learn'-language in schools and universities). For example:
Code: Select all
Special
name = "WORLDTREE_SPECIAL"
description = "WORLDTREE_SPECIAL_DESC"
stealth = 0
spawnrate = 1.0
spawnlimit = 1
location = And [
Planet
Not Planet type = [Asteroids GasGiant Inferno]
Not Planet size = [tiny huge]
Not WithinStarlaneJumps jumps = 2 condition = And [
System
Contains And [
Planet
OwnedBy affiliation = AnyEmpire
]
]
]
effectsgroups = [
EffectsGroup
scope = Source
activation = Source
accountinglabel = "WORLDTREE_LABEL"
priority = [[LATE_PRIORITY]]
effects = [
SetDetection value = Value + 10
SetMaxSupply value = Value + 1
SetTargetHappiness value = Value + 5
]
EffectsGroup
scope = Source
activation = TargetPopulation low = 1
accountinglabel = "WORLDTREE_LABEL"
priority = [[LATE_PRIORITY]]
effects = SetTargetPopulation value = Value + 1
EffectsGroup
scope = And [
Planet
Not Source
OwnedBy affiliation = TheEmpire empire = Source.Owner
Population low = 0.0001
]
effects = SetTargetHappiness value = Value + 1
]
graphic = "icons/specials_huge/worldtree.png"
Code: Select all
class WorldTree(Special):
name = "WORLDTREE_SPECIAL"
description = "WORLDTREE_SPECIAL_DESC"
stealth = 0
spawnrate = 1.0
spawnlimit = 1
location = And(
IsPlanet,
Not(IsPlanetType([PlanetType.Asteroids, PlanetType.GasGiant, PlanetType.Inferno])),
Not(IsPlanetSize([PlanetSize.TINY, PlanetSize.HUGE])),
Not(WithinStarlaneJumps(jumps = 2, condition = And(
System,
Contains(And(
IsPlanet,
OwnedBy(Affiliation.AnyEmpire)
))
)))
)
effectsgroups = [
EffectsGroup(
scope = Source,
activation = Source,
accountinglabel = "WORLDTREE_LABEL"
priority = common.late_priority,
effects = [
Set("Detection", Add(Value, 10)),
Set("MaxSupply", Add(Value, 1)),
Set("TargetHappiness", Add(Value, 5))
]
),
EffectsGroup(
scope = Source,
activation = InLimit("TargetPopulation", lower = 1),
accountinglabel = "WORLDTREE_LABEL",
priority = common.late_priority,
effects = Set("TargetPopulation", Add(Value, 1))
),
EffectsGroup(
scope = And(
IsPlanet,
Not(Source),
OwnedBy(Affilitation.TheEmpire, empire = Source.Owner),
InLimit("Population", lower = 0.0001)
),
effects = Set("TargetHappiness", Add(Value, 1))
)
]
graphic = "icons/specials_huge/worldtree.png"